Promise와 async/await

breakfast_wuΒ·2022λ…„ 3μ›” 29일
0

πŸ“Œ λ“€μ–΄κ°€λ©°

이전 κΈ€μ—μ„œ μ‚΄νŽ΄λ΄€λ“― μžλ°”μŠ€ν¬λ¦½νŠΈλŠ” μ‹±κΈ€ μŠ€λ ˆλ“œλ‘œ λ™μž‘ν•˜λŠ” μ–Έμ–΄λ‘œμ„œ ν•œλ²ˆμ— ν•˜λ‚˜μ˜ μž‘μ—…λ§Œ μˆ˜ν–‰ν•  수 μžˆλ‹€.
이λ₯Ό ν•΄κ²°ν•˜κΈ° μœ„ν•΄ μžλ°”μŠ€ν¬λ¦½νŠΈμ˜ λŸ°νƒ€μž„ ν™˜κ²½μ˜ 도움을 λ°›μ•„ 비동기 μž‘μ—…μ„ μˆ˜ν–‰ν•¨μœΌλ‘œμ¨
μ‹±κΈ€μŠ€λ ˆλ“œ-λ…ΌλΈ”λ‘œν‚Ή λͺ¨λΈμ„ κ΅¬ν˜„ν•  수 있게 λ˜μ—ˆλ‹€.
μ΄μ œλΆ€ν„° λŸ°νƒ€μž„ ν™˜κ²½μ΄ μ–΄λ–»κ²Œ 비동기λ₯Ό μ²˜λ¦¬ν•˜λŠ”μ§€ μ•Œμ•„λ³΄μž.


1. 콜백 ν•¨μˆ˜ (callback function)

콜백 ν•¨μˆ˜λŠ” 말 κ·ΈλŒ€λ‘œ λ‚˜μ€‘μ— ν˜ΈμΆœλ˜λŠ” ν•¨μˆ˜λ‹€. λ…Έλ“œλŠ” 이벀트 기반(event-driven) λ°©μ‹μœΌλ‘œ λ™μž‘ν•˜κΈ° λ•Œλ¬Έμ—, μ΄λ²€νŠΈκ°€ λ°œμƒν•˜λ©΄ 이벀트 λ¦¬μŠ€λ„ˆμ— 등둝해둔 콜백 ν•¨μˆ˜λ₯Ό ν˜ΈμΆœν•œλ‹€. ν•΄λ‹Ή λ™μž‘μ΄ μ™„λ£Œλ  λ•Œ κΉŒμ§€ 직접 기닀릴 ν•„μš” 없이 λ™μž‘μ„ μ‹€ν–‰μ‹œμΌœ 놓고, μ™„λ£Œλμ„ λ•Œ κ²°κ³Όλ₯Ό λ°›λŠ” ν˜•μ‹μ΄λ‹€.호좜된 콜백 ν•¨μˆ˜λŠ” libuv의 event queue둜 λ„˜μ–΄κ°€κ³  이벀트 루프, 콜백 큐λ₯Ό 거쳐 μ΅œμ’… 응닡을 ν•œλ‹€.

콜백 μ§€μ˜₯ (callback hell)

λ‹¨μˆœν•œ 콜백 처리라면 λ¬Έμ œκ°€ μ—†κ² μ§€λ§Œ, μ‹€μ œ μ½”λ“œλ₯Ό μž‘μ„±ν•  λ•Œ μ‹€ν–‰ μˆœμ„œλ₯Ό μ‹ κ²½ μ¨μ•Όν•˜λŠ” 상황이 λŒ€λΆ€λΆ„μ΄λ‹€.
μ΄λŸ΄λ•Œ λ§ˆλ‹€ 콜백이 μ€‘μ²©λœλ‹€λ©΄ μ½”λ“œμ˜ κΉŠμ΄κ°€ κ³„μ†ν•΄μ„œ κΉŠμ–΄μ§€λŠ” 참사가 λ°œμƒν•œλ‹€.
λ‹€μŒ μ½”λ“œλ₯Ό 보자. [μ½”λ“œ 좜처]

function 학생정보_쑰회(학생_ν•™λ²ˆ, callback1) {
    ajax(
        baseUrl + "student-info/" + 학생_ν•™λ²ˆ, function (response) {
        callback1(response);
    });
}

function κ³ κ΅μ£Όμ†Œ_쑰회(고ꡐλͺ…, callback2) {
    ajax(
        baseUrl + "highschoolDB/" + 고ꡐλͺ…, function (response) {
        callback2(response);
    });
}

function 고ꡐ_μˆ˜μ—…_쑰회(고ꡐ_DB_μ£Όμ†Œ, callback3) {
    ajax(
        baseUrl + "classes/" + 고ꡐ_DB_μ£Όμ†Œ, function (response) {
        callback3(response);
    });
}

function μˆ˜μ—…_정보_쑰회(κ³ 3_μˆ˜ν•™μˆ˜μ—…_μ½”λ“œ, callback4) {
    ajax(
        baseUrl + "class-info/" + κ³ 3_μˆ˜ν•™μˆ˜μ—…_μ½”λ“œ, function (response) {
        callback4(response);
    });
}


function κ³ 3_μˆ˜ν•™κ΅μ‚¬_μ°ΎκΈ°(학생_ν•™λ²ˆ) {
    학생정보_쑰회(학생_ν•™λ²ˆ, 
        function(학생정보) {
            let ν•™μƒμ£Όλ―Όλ²ˆν˜Έ = 학생정보["주민번호"];
            let 고ꡐλͺ… = 학생정보["고등학ꡐλͺ…"];
            κ³ κ΅μ£Όμ†Œ_쑰회(고ꡐλͺ…, 
                function(고ꡐ_DB_μ£Όμ†Œ) {
                    고ꡐ_μˆ˜μ—…_쑰회(고ꡐ_DB_μ£Όμ†Œ, ν•™μƒμ£Όλ―Όλ²ˆν˜Έ, 
                        function(κ³Όλͺ©μ•ŒλžŒ) {
                            let κ³ 3_μˆ˜ν•™μˆ˜μ—…_μ½”λ“œ = κ³Όλͺ©μ•ŒλžŒ["κ³ 3μˆ˜ν•™"];
                            μˆ˜μ—…_정보_쑰회(κ³ 3_μˆ˜ν•™μˆ˜μ—…_μ½”λ“œ, 
                                function(μˆ˜μ—…μ •λ³΄) {
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> console.log(`담당ꡐ사: ${μˆ˜μ—…μ •λ³΄["ꡐ사λͺ…"]}`);
                                }
                            )
                        }
                    )
                }
            )
        }
    )
}

κ³ 3_μˆ˜ν•™κ΅μ‚¬_μ°ΎκΈ°(123456);

ν•™μƒμ˜ ν•™λ²ˆμœΌλ‘œ κ³ 3 μ‹œμ ˆ μˆ˜ν•™κ΅μ‚¬λ₯Ό μ°ΎλŠ” μ½”λ“œμ΄λ‹€.
μ € 말도 μ•ˆλ˜λŠ” μ½”λ“œμ˜ 깊이λ₯Ό 각자 λœ―μ–΄λ³΄λ©° κ°μƒν•΄λ³΄μž.
μ΄λ ‡κ²Œ 콜백이 쀑첩될 λ•Œ λ§ˆλ‹€ μ½”λ“œκ°€ μ•ˆμœΌλ‘œ λ“€μ–΄κ°€κ²Œ 되고,
μ΄λŠ” μ½”λ“œμ˜ 가독성을 ν•΄μΉ  뿐 μ•„λ‹ˆλΌ 디버깅 μž‘μ—… λ˜ν•œ μ§€μ˜₯이 λ˜μ–΄λ²„λ¦°λ‹€.


2. Promise

콜백 μ§€μ˜₯μ—μ„œ λ²—μ–΄λ‚˜κΈ° μœ„ν•΄ ES6λΆ€ν„° λ„μž…λœ 객체둜, μžλ°”μŠ€ν¬λ¦½νŠΈμ™€ λ…Έλ“œμ˜ API듀이 콜백 λŒ€μ‹  ν”„λ‘œλ―ΈμŠ€ 기반으둜 μž¬κ΅¬μ„± λ˜μ—ˆλ‹€. ν”„λ‘œλ―ΈμŠ€λ₯Ό μ‚¬μš©ν•˜λ©΄ 비동기 λ©”μ„œλ“œμ—μ„œ 마치 동기 λ©”μ„œλ“œμ²˜λŸΌ 값을 λ°˜ν™˜ν•  수 μžˆλ‹€. 미래의 μ–΄λ–€ μ‹œμ μ— κ²°κ³Όλ₯Ό μ œκ³΅ν•˜κ² λ‹€λŠ” "약속(ν”„λ‘œλ―ΈμŠ€)"λ₯Ό λ°˜ν™˜ν•˜λŠ” 것이닀. 그리고 이 약속은 then 을 톡해 λ°›λŠ”λ‹€.

function 학생정보_쑰회_Promise(학생_ν•™λ²ˆ) {
    return new Promise((resolve, reject) => {
        ajax(
            baseUrl + "student-info/" + 학생_ν•™λ²ˆ, function (response) {
            resolve(response);
        });
    })
}

function κ³ κ΅μ£Όμ†Œ_쑰회_Promise(ν•™μƒμ£Όλ―Όλ²ˆν˜Έ, 고ꡐλͺ…) {
    return new Promise((resolve, reject) => {
        ajax(
            baseUrl + "highschoolDB/" + 고ꡐλͺ…, function (response) {
            resolve([ν•™μƒμ£Όλ―Όλ²ˆν˜Έ,response]);
        });
    })
}

function 고ꡐ_μˆ˜μ—…_쑰회_Promise(ν•™μƒμ£Όλ―Όλ²ˆν˜Έ, 고ꡐ_DB_μ£Όμ†Œ) {
    return new Promise((resolve, reject) => {
        ajax(
            baseUrl + "classes/" + 고ꡐ_DB_μ£Όμ†Œ + "/" + ν•™μƒμ£Όλ―Όλ²ˆν˜Έ, function (response) {
            resolve(response);
        });
    })
}

function μˆ˜μ—…_정보_쑰회_Promise(κ³ 3_μˆ˜ν•™μˆ˜μ—…_μ½”λ“œ) {
    return new Promise((resolve, reject) => {
        ajax(
            baseUrl + "class-info/" + κ³ 3_μˆ˜ν•™μˆ˜μ—…_μ½”λ“œ, function (response) {
            resolve(response);
        });
    })
}

학생정보_쑰회_Promise(123456)
    .then(학생정보 => {
        let ν•™μƒμ£Όλ―Όλ²ˆν˜Έ = 학생정보["주민번호"];
        let 고ꡐλͺ… = 학생정보["고등학ꡐλͺ…"];
        return κ³ κ΅μ£Όμ†Œ_쑰회_Promise(ν•™μƒμ£Όλ―Όλ²ˆν˜Έ, 고ꡐλͺ…);
    })
    .then(ν•™μƒμ£Όλ―Όλ²ˆν˜Έ_고ꡐDBμ£Όμ†Œ => {
        return 고ꡐ_μˆ˜μ—…_쑰회_Promise(ν•™μƒμ£Όλ―Όλ²ˆν˜Έ_고ꡐDBμ£Όμ†Œ[0], ν•™μƒμ£Όλ―Όλ²ˆν˜Έ_고ꡐDBμ£Όμ†Œ[1]);
    })
    .then(μˆ˜κ°•κ³Όλͺ©μ•ŒλžŒ => {
        let κ³ 3_μˆ˜ν•™μˆ˜μ—…_μ½”λ“œ = κ³Όλͺ©μ•ŒλžŒ["κ³ 3μˆ˜ν•™"];
        return μˆ˜μ—…_정보_쑰회_Promise(κ³ 3_μˆ˜ν•™μˆ˜μ—…_μ½”λ“œ);
    })
    .then(μˆ˜μ—…μ •λ³΄ => {
        console.log(`담당ꡐ사: ${μˆ˜μ—…μ •λ³΄["ꡐ사λͺ…"]}`);
    })

ν”„λ‘œλ―ΈμŠ€λ₯Ό λ°˜ν™˜ν•˜λ„λ‘ ν•˜μ—¬, then λ©”μ„œλ“œμ˜ 체이닝을 톡해 μ’€ 더 κΉ”λ”ν•˜ μ½”λ“œλ₯Ό λ§Œλ“€ 수 있게 λ˜μ—ˆλ‹€.
ν”„λ‘œλ―ΈμŠ€ λ‚΄λΆ€μ—μ„œ resolveκ°€ 호좜되면 then이 μ‹€ν–‰λ˜κ³ , rejectκ°€ 호좜되면 catchκ°€ μ‹€ν–‰λœλ‹€.
ν•˜μ§€λ§Œ 아직도 일련의 쒅속적인 μž‘μ—…λ“€μ΄ ν•˜λ‚˜μ˜ ν”„λ‘œλ―ΈμŠ€ 체인에 λ¬Άμ—¬μžˆλ‹€.


3. async/await

async/awaitλ₯Ό μ‚¬μš©ν•˜λ©΄ 비동기 ν•¨μˆ˜λ₯Ό 마치 동기적인 μ½”λ“œμΈ 것 처럼 λ™μž‘ν•˜λ„λ‘ κ΅¬ν˜„ν•  수 μžˆλ‹€.

function 학생정보_쑰회_Promise(학생_ν•™λ²ˆ) {
    return new Promise((resolve, reject) => {
        ajax(
            baseUrl + "student-info/" + 학생_ν•™λ²ˆ, function (response) {
            resolve(response);
        });
    })
}

function κ³ κ΅μ£Όμ†Œ_쑰회_Promise(ν•™μƒμ£Όλ―Όλ²ˆν˜Έ, 고ꡐλͺ…) {
    return new Promise((resolve, reject) => {
        ajax(
            baseUrl + "highschoolDB/" + 고ꡐλͺ…, function (response) {
            resolve([ν•™μƒμ£Όλ―Όλ²ˆν˜Έ,response]);
        });
    })
}

function 고ꡐ_μˆ˜μ—…_쑰회_Promise(ν•™μƒμ£Όλ―Όλ²ˆν˜Έ, 고ꡐ_DB_μ£Όμ†Œ) {
    return new Promise((resolve, reject) => {
        ajax(
            baseUrl + "classes/" + 고ꡐ_DB_μ£Όμ†Œ + "/" + ν•™μƒμ£Όλ―Όλ²ˆν˜Έ, function (response) {
            resolve(response);
        });
    })
}

function μˆ˜μ—…_정보_쑰회_Promise(κ³ 3_μˆ˜ν•™μˆ˜μ—…_μ½”λ“œ) {
    return new Promise((resolve, reject) => {
        ajax(
            baseUrl + "class-info/" + κ³ 3_μˆ˜ν•™μˆ˜μ—…_μ½”λ“œ, function (response) {
            resolve(response);
        });
    })
}

async function κ³ 3_μˆ˜ν•™κ΅μ‚¬_μ°ΎκΈ°(학생_ν•™λ²ˆ) {
    let 학생정보 = await 학생정보_쑰회_Promise(학생_ν•™λ²ˆ);
    let 고ꡐ_DB_μ£Όμ†Œ = await κ³ κ΅μ£Όμ†Œ_쑰회_Promise(학생정보["주민번호"], 학생정보["고등학ꡐλͺ…"]);
    let μˆ˜κ°•κ³Όλͺ©μ•ŒλžŒ = await 고ꡐ_μˆ˜μ—…_쑰회_Promise(학생정보["주민번호"], 고ꡐ_DB_μ£Όμ†Œ);
    let μˆ˜μ—…μ •λ³΄ = await μˆ˜μ—…_정보_쑰회_Promise(μˆ˜κ°•κ³Όλͺ©μ•ŒλžŒ["κ³ 3μˆ˜ν•™"]);
    console.log(`담당ꡐ사: ${μˆ˜μ—…μ •λ³΄["ꡐ사λͺ…"]}`);
}

λ†€λΌμšΈ μ •λ„λ‘œ μ½”λ“œκ°€ μ§§μ•„μ‘Œλ‹€.
ν•¨μˆ˜μ— async ν‚€μ›Œλ“œλ₯Ό 뢙이면 μžλ™μœΌλ‘œ Promiseλ₯Ό λ¦¬ν„΄ν•˜λŠ” ν•¨μˆ˜λ‘œ λ§Œλ“€μ–΄μ€€λ‹€.
그리고 await을 뢙이면 ν•΄λ‹Ή ν”„λ‘œλ―ΈμŠ€κ°€ resolve 될 λ•Œ κΉŒμ§€ κΈ°λ‹€λ Έλ‹€κ°€ κ²°κ³Όλ₯Ό λ°›μœΌλ©΄ λ‹€μŒ 둜직으둜 λ„˜μ–΄κ°„λ‹€.
즉, Promise 객체의 then κ²°κ³Όλ₯Ό λ°”λ‘œ λ°›λŠ” 것이닀.

  • asyncκ°€ 뢙은 ν•¨μˆ˜λŠ” ν”„λ‘œλ―ΈμŠ€λ₯Ό λ°˜ν™˜ν•˜κ³ , ν”„λ‘œλ―ΈμŠ€κ°€ μ•„λ‹Œ 것은 ν”„λ‘œλ―ΈμŠ€λ‘œ 감싸 λ°˜ν™˜ν•œλ‹€.
  • await ν‚€μ›Œλ“œλ₯Ό λ§Œλ‚˜λ©΄ ν”„λ‘œλ―ΈμŠ€κ°€ 처리될 λ•Œ κΉŒμ§€ κΈ°λ‹€λ¦°λ‹€.
  • await ν‚€μ›Œλ“œλŠ” asyncκ°€ 뢙은 ν•¨μˆ˜ μ•ˆμ—μ„œλ§Œ μ‚¬μš© κ°€λŠ₯ν•˜λ‹€. ( Promise.then( ) 을 합쳐놓은 λ†ˆμ΄λΌκ³  봐도 λœλ‹€. )

async/await 을 μ‚¬μš©ν•˜λ©΄ awaitκ°€ λŒ€κΈ°λ₯Ό μ²˜λ¦¬ν•΄μ£ΌκΈ° λ•Œλ¬Έμ— .then이 거의 ν•„μš”ν•˜μ§€ μ•Šλ‹€.
λ˜ν•œ promise.catch λŒ€μ‹  try/catchλ₯Ό μ‚¬μš©ν•  수 있게 λœλ‹€.


async μ΅œμ ν™”

πŸš€ 각 비동기 μž‘μ—…μ΄ μ‹€ν–‰ μˆœμ„œμ— 영ν–₯을 μ€€λ‹€λ©΄ await을 톡해 κΈ°λ‹€λ¦¬λŠ” 것이 ν•©λ‹Ήν•˜μ§€λ§Œ μˆœμ„œμ™€ κ΄€κ³„μ—†λŠ”, λ™μ‹œμ— 처리될 수 μžˆλŠ” μž‘μ—…μ΄λΌλ©΄ μ΅œλŒ€ν•œ λ™μ‹œμ— μ‹€ν–‰ 될 수 μžˆλ„λ‘ ν•΄μ•Όν•œλ‹€.

function delay(){
	return new Promise( (resolve, reject) => {
		setTimeout(() => resolve(), 1000);
	})
}

async function getApple(){
	await delay(); // 1초 λ”œλ ˆμ΄ μ£ΌκΈ°
	return "apple";
}

async function getBanana(){
	await delay(); // 1초 λ”œλ ˆμ΄ μ£ΌκΈ°
	return "banana";
}

async function getFruites(){
	let getApplePromise = getApple(); // asyncν•¨μˆ˜λ₯Ό μ‹€ν–‰μ‹œν‚¨λ‹€. λ…ΌλΈ”λ‘ν‚ΉμœΌλ‘œ λ°±λ‹¨μ—μ„œ μ‹€ν–‰λ˜κ²Œ λœλ‹€.
	let getBananaPromise = getBanana(); // λ¦¬ν„΄μœΌλ‘œ ν”„λ‘œλ―ΈμŠ€ 객체둜 감싸진 결과값을 λ°›λŠ”λ‹€.
	
  	let a = await getApplePromise; // λ¦¬ν„΄μœΌλ‘œ 받은 ν”„λ‘œλ―ΈμŠ€κ°μ²΄λ₯Ό done(data)λ₯Ό await으둜 λΉΌμ„œ λ³€μˆ˜μ— λ„£μŒ
	let b = await getBananaPromise;

	console.log(`${a} and ${b}`);
}

getFruites(); // κ²°κ³Ό : apple and banana

getAppleκ³Ό getBananaλŠ” μ„œλ‘œ 영ν–₯을 μ£Όμ§€ μ•ŠλŠ” 관계이닀.
λ”°λΌμ„œ 두 ν•¨μˆ˜λ₯Ό λ…Ό λΈ”λ‘œν‚Ή λ°©μ‹μœΌλ‘œ ν˜ΈμΆœν•˜κ³  각각의 ν•¨μˆ˜λŠ” ν”„λ‘œλ―ΈμŠ€λ₯Ό λ°˜ν™˜ν•œλ‹€. (pending μƒνƒœ)
그리고 κ²°κ³ΌλŠ” await ν‚€μ›Œλ“œλ₯Ό 톡해 μ•„λž˜μͺ½μ—μ„œ λ°›λŠ”λ‹€.
μ΄λ ‡κ²Œ λ³‘λ ¬μ μœΌλ‘œ μ²˜λ¦¬ν•œλ‹€λ©΄ await getApple(), await getBanana() λ₯Ό ν–ˆμ„ λ•Œ 보닀 훨씬 더
μ„±λŠ₯ 쒋은 μ½”λ“œλ₯Ό μž‘μ„±ν•  수 μžˆλ‹€.

좜처: https://inpa.tistory.com/entry/JS-πŸ“š-λΉ„λ™κΈ°μ²˜λ¦¬-async-await [πŸ‘¨β€πŸ’» Dev Scroll]


정리

Node.jsλŠ” V8μ—”μ§„ 기반으둜 λ™μž‘ν•˜λ©° λ‚΄λΆ€μ˜ Event LoopλŠ” Single-Thread κΈ°λ°˜μ—μ„œ 비동기 μž‘μ—…μ„ μ²˜λ¦¬ν•œλ‹€.
μ΄λŸ¬ν•œ 이벀트 λ£¨ν”„λŠ” κ³ μ„±λŠ₯의 λ³‘λ ¬μ²˜λ¦¬λ₯Ό 보μž₯ν•˜λ„λ‘ μ„€κ³„λ˜μ–΄ μžˆλ‹€. λ”°λΌμ„œ μ΄λ²€νŠΈμ— μ˜ν•΄ μ²˜λ¦¬ν•΄μ•Ό ν•  λ‹¨μœ„ μž‘μ—…μ΄
μ•„μ£Ό 짧은 μ‹œκ°„ μ•ˆμ— μ²˜λ¦¬λœλ‹€λ©΄ Node.js의 μž₯점을 κ·ΉλŒ€ν™” ν•  수 μžˆλ‹€.

κ·Έλ ‡κΈ° λ•Œλ¬Έμ— Node μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ€ κ°€λŠ₯ν•œ ν•œ μ „λΆ€ λΉ„λ™κΈ°λ‘œ μ²˜λ¦¬ν•΄μ•Ό ν•˜λ©° λ™μ‹œμ— 처리될 수 μžˆλŠ” I/O μž‘μ—…μ΄λΌλ„
λ…Ό λΈ”λ‘œν‚Ή λ°©μ‹μœΌλ‘œ μ½”λ”©ν•˜μ§€ μ•ŠμœΌλ©΄ μ˜λ―Έκ°€ ν‡΄μƒ‰λœλ‹€. λ”°λΌμ„œ λ™μ‹œμ²˜λ¦¬κ°€ κ°€λŠ₯ν•œ μž‘μ—…μ€ μ΅œλŒ€ν•œ λ¬Άμ–΄μ„œ ν•΄κ²°ν•  수 μžˆλ„λ‘ μ½”λ”©ν•˜λŠ” μŠ΅κ΄€μ„ λ“€μ—¬μ•Ό ν•œλ‹€.


[참고자료]

0개의 λŒ“κΈ€