27. 비동기 처리 - promise, then, catch, async, await

JSLEE·2024년 5월 30일

지난 포스트에서, 하나의 엔드포인트에 3개의 쿼리문을 수행하려고 했다.
1. deliveries테이블에 INSERT
2. deliveries를 참조한 orders 테이블에 INSERT
3. orders를 참조한 ordered_books 테이블에 INSERT

그래서, 각 쿼리를 수행할 때마다 results.insertId를 통해 방금 입력한 ID 값을 기억하려고 했었다.

하지만 여기에는 Node.js의 특징에서 발생하는 문제가 있다.
Node.js 의 특징

  • Single Thread (싱글스레드)
  • Non-Blocking I/O (논블로킹 I/O)
  • Event-Driven (이벤트기반)

이 중에서도, Non-Blocking I/O 를 주목해야한다.
Non-Blocking이란, 여러개의 작업이 주어졌을 때 한 작업이 끝날 때 까지 기다리지 않고 다른 작업도 수행한다는 의미이다.

A,B,C 라는 작업이 주어졌을 때, A작업이 끝나지 않은채로 B,C 작업도 동시에 수행한다는 의미이다.
그러면 우리가 마주한 상황인 세 테이블에서의 INSERT 에서도,
results.InsertId 값이 우리가 예상한 대로 들어오지 않고 undefined인 상태이게 된다.

let delivery_id ;
let order_id;

conn.query(sql, value, (err,results)=>{
 delivery_id = results.insertId // undefined
})

conn.query(sql, value, (err,results)=>{
 order_id = results.insertId // undefined
})

conn.query(sql, value, (err,results)=>{
})

따라서, 우리는 하나의 쿼리문을 완전히 실행하고 난 뒤에야 다음 쿼리를 수행하도록 비동기에 대한 처리를 해줘야한다.

비동기 처리에는 다음의 4가지 개념이 필요하다.

  1. callback
  2. promise (resolve, reject)
  3. then & catch
  4. async & await

callback함수에 대해서는 잘 아는 내용이니, promise부터 알아보도록 하겠다.


promise

  1. Promise는 비동기 처리를 위한 하나의 객체이다.
    따라서, Promise 객체를 생성할 때 다음과 같이 코드를 작성할 수 있다.
let promise = new Promise() ;

다만, 다음과 같이 Promise 생성자에는 수행할 비동기작업에 대한 콜백함수를 전달받고,
콜백함수에는 resolve, reject 라는 두개의 매개변수를 가진다.

let promise = new Promise((resolve, reject) => {}) ;

Promise의 콜백함수에서 성공적으로 수행한다면 resolve("값")에 값을 전달하고, 에러가 발생한다면 reject("값")에 값을 전달한다.

let promise = new Promise((resolve, reject) => {
	let ok = true;
  	if (ok) {
    	setTimeout(()=> resolve("완료"), 3000);
    } else {
    	setTimeout(()=> reject("에러 발생"), 3000);
    }
}) ;

위의 코드를 실행하면, 아무런 일도 발생하지 않는다.
promise는 then()catch()와 같이 사용되어야 한다.


then( ), catch( )

결론부터 말하자면 resolvethen과 관련되고, rejectcatch와 관련된다.
코드를 먼저 살펴보자.

1. then, catch 이용

let promise = new Promise((resolve, reject) => {
	let ok = true;
  	if (ok) {
    	setTimeout(()=> resolve("완료"), 3000);
    } else {
    	setTimeout(()=> reject("에러 발생"), 3000);
    }
}) ;

promise
  .then( (data)=>{console.log(data)} )  // 완료
  .catch( (data)=>{console.log(data)} ) // 에러 발생

// 결과 : 완료

then과 catch에도 매개변수로 callback 함수가 필요하고,
이 callback 함수는 매개변수로 resolve( )와 reject( )에 넣어준 값을 갖는다.
위 코드는 무조건 참인 상황이므로, then( ) 문장은 수행되지만, catch 문장은 수행되지 않을 것이다.

2. then 이용
then(), catch()를 사용하지 않고, then()만 사용해서도 똑같이 동작시킬수 있다.

let promise = new Promise((resolve, reject) => {
	let ok = true;
  	if (ok) {
    	setTimeout(()=> resolve("완료"), 3000);
    } else {
    	setTimeout(()=> reject("에러 발생"), 3000);
    }
}) ;

promise
  .then( (data)=>{console.log(data)} , (data)=>{console.log(data)} )

// 결과 : 완료

위의 코드처럼, then의 매개변수로 콜백함수를 2개 사용하면 된다.
첫번째 콜백함수는 Promise가 정상적으로 수행됐을 때로, resolve에 입력한 값을 매개변수로 전달받고,
두번째 콜백함수는 Promise에서 오류가 발생했을 때로, reject에 입력한 값을 매개변수로 전달받는다.

1번 방법(then+catch)의 특징은, *promise-chaining에서 발생하는 모든 reject에 대하여 catch문 하나로 모든 오류를 처리할 수 있다는 특징이 있다.
2번 방법(then)의 특징은, promise-chaining에서 발생하는 reject에 대하여 각각의 오류를 처리할 수 있다는 특징이 있다.

*Promise-Chaining

Promise-Chaining이란, 여러개의 Promise 를 연결해서 순차적으로 실행될 수 있도록 하는 것이다.
아래 코드를 살펴보자.

let promise = new Promise((resolve, reject) => {
  setTimeout( ()=> resolve("첫번째"), 3000)
})
  .then((result) => {
    console.log(result) // 첫번째
    return new Promise((resolve, reject) => {
       setTimeout( ()=> resolve("두번째"), 3000)
    })
  })
  .then((result) => {
    console.log(result) // 두번째
  })

/* 결과 : 
첫번째
두번째

*/

promise 객체를 중첩하는 것은 간단하다. then에서 return 값에 새로운 promise 객체를 생성해주면 된다.
이렇게 promise 객체가 순차적으로 수행되어 비동기를 처리하는 것을 promise-chaining 이라고 부른다.


async, await

async, await은 Promise 객체를 좀 더 쉽고 편하게 다루도록 해주는 키워드이다.

asyncawait을 사용함에 있어서, 몇가지 알아두어야 할 사항들이 있다.

  1. async함수와 같이 사용된다.
    ex)
async function f() {
	return "async"
}
  1. async는 항상 Promise 객체를 반환한다. (따라서, then, catch 구문을 쓸 수 있다.)
async function f() {
	return "async" // Promise.resolve("async") 와 같은 표현.
}
f().then( (data)=>{console.log(data)} , (error) => {console.log(error)})
  1. await은 단독으로 사용할 수 없고, async내부에서 사용해야 한다.

  2. await은 promise가 resolve되거나 reject될 때까지 대기한다.
    ex)

async function f() {
  let promise = new Promise(function (resolve, reject) {
    setTimeout(() => resolve("첫번째"), 3000)
  })
  let result = await promise; // 3초 대기
  console.log(result) // "첫번째"
}
profile
공부한 내용들을 정리하기 위해 사용하는 블로그입니다.

0개의 댓글