JS - Promise

Lee Ju-Hyeon(David)·2021년 9월 30일
0

Java Script

목록 보기
7/8
post-thumbnail

동기식 : 응답을 받아야만 다음 동작을 이어가는 방식
비동기식 : 응답 여부와 관계 없이 다음 동작을 이어가는 방식

자바스크립트는 기본적으로 동기식으로 작동한다. 하나의 Call Stack만 사용하기 때문에, JS만으로는 비동기식 동작을 구현할 수 없다. 따라서 브라우저에서 지원하는 Web API를 이용해서 비동기식 동작을 구현하게 된다. setTimeout이나 axois를 이용해 요청을 보내고 응답을 기다려야 할 경우, 이 함수는 Web API로 넘어가게 되고 다른 함수가 Call Stack으로 들어와 실행되는데, 이를 오히려 방지해야 하는 경우가 있다.

자바스크립트의 동작 원리 참고

function a() {
  setTimeout(() => {
    console.log('A')
  }, 1000);
}

function b() {
  console.log('B')
}

function test() {
  a()
  b()
}
test()
// B
// A

1. callback

함수의 실행의 순서를 보장하고 싶을 때 사용할 수 있는 방법 중에 하나로 callback이 있다.

function a(callback) {
  setTimeout(() => {
    console.log('A')
    callback()
  }, 1000);
}

function b(callback) {
  console.log('B')
  callback()
}

function c(callback) {
  setTimeout(() => {
    console.log('C')
    callback()
  }, 1000);
}

function d(callback) {
  console.log('D')
  callback()
}

function test() {
  a(() => {
    b(() => {
      c(() => {
        d(() => {
        })
      })
    })
  })
}
test()
// A
// B
// C
// D

callback을 사용하지 않고 실행하면 실행하는데 딜레이가 있는 a함수보다 b함수가 먼저 실행되겠지만, 비동기 실행을 했기 때문에 순서대로 실행이 되었다. 하지만 callback을 여러 개 중첩해서 사용할 경우 코드가 복잡해지고 관리가 어려워진다. 위 코드처럼 순차적으로 되어 있지 않고 조금만 복잡해도 알아보기 힘들 것이다.


2. Promise

Promise 참고

callback 함수를 사용해 순서를 보장해 주는 것처럼 함수 실행의 순서를 보장할 수 있는 방법으로 Promise 객체를 사용하는 방법이 있다.

2.1 비동기 실행

function a() {
  return new Promise((resolve) => {
    setTimeout(() => {
      console.log('A')
      resolve()
    }, 1000)
  })
}

function b() {
  return new Promise((resolve) => {
    console.log('B')
    resolve()
  })
}

function c() {
  return new Promise((resolve) => {
    setTimeout(() => {
      console.log('C')
      resolve()
    }, 1000)
  })
}

function d() {
  return new Promise((resolve) => {
    console.log('D')
    resolve()
  })
}

function test() {
  a().then(() => {
    b().then(() => {
      c().then(() => {
        d().then(() => {
          console.log('Done')
        })
      })
    })
  })
}
test()

a, b, c, d 함수는 각각 Promise 객체를 반환한다. Promise 객체의 매개변수로 하나의 함수가 들어가고, 해당 함수의 매개변수로 resolve가 할당된다. 이 resolve는 callback과 대응한다고 볼 수 있다.

a 함수 내부에서 resolve 매개변수가 실행될 때까지 앞의 코드가 실행되고, resolve가 실행되면 Promise 객체를 반환한 뒤 then메서드를 실행한다.

하지만, 위 코드를 보면 callback을 사용했을 때와 유사하게 복잡하다. 이를 return을 이용해서 수정하면 아래처럼 만들 수 있다.

function test() {
  a().then(() => {
    return b()
  }).then(() => {
    return c()
  }).then(() => {
    return d()
  }).then(() => {
    console.log('Done')
  })
}

a함수를 실행하면 Promise 객체 하나를 반환한다. then 메서드를 실행하여 a함수의 실행이 끝난 뒤 b함수를 실행하여 반환한다. 즉, b함수의 Promise 객체가 반환된다. 마지막에 반환값 없이 함수를 실행하고 끝난다.

2.2 예외처리

Promise 객체는 catchfinally메서드를 활용해서 예외처리가 가능하다.

function a(number) {
  return new Promise((resolve, reject) => {
    if (number > 4) {
      reject()
      return
    }
    setTimeout(() => {
      console.log('A')
      resolve()
    }, 1000)
  })
}

function test() {
  a([인수])
    .then(() => {
      console.log('resolve')
    })
    .catch(() => {
      console.log('reject')
    })
    .finally(() => {
      console.log('done')
    })
}

a함수부터 살펴 보면 매개변수로 reject가 추가되었다. rejectresolve가 실행될 수 없는 상황에서 실행되는 매개변수로 catch메서드를 실행시킨다. 위 코드를 예로 살펴보면, a함수의 number매개변수로 4이하의 값이 할당되면 resolve매개변수가 실행되어 결과는 다음 과 같다.

A
resolve
done

반대로 a함수의 number매개변수로 4보다 큰 값이 할당되면 reject매개변수가 실행되어 결과는 다음 과 같다.

reject
done

결과를 보면 finally라는 메서드는 resolvereject가 실행됐을 때 모두 동작하는 것을 알 수 있다. finally는 항상 실행되는 메서드이다.


3. async, await

최신의 자바스크립트는 async함수와 await를 제공한다. 이 또한 함수 실행 순서를 보장해주는 역할을 하는데 callback이나 Promise 객체의 메서드를 사용할 때 보다 훨씬 직관적인 코드를 작성할 수 있다.

3.1 비동기 실행

function a() {
  return new Promise((resolve) => {
    setTimeout(() => {
      console.log('A')
      resolve()
    }, 1000)
  })
}

function b() {
  return new Promise((resolve) => {
    console.log('B')
    resolve()
  })
}

function c() {
  return new Promise((resolve) => {
    setTimeout(() => {
      console.log('C')
      resolve()
    }, 1000)
  })
}

function d() {
  return new Promise((resolve) => {
    console.log('D')
    resolve()
  })
}

async function test() {
  await a()
  b()
  await c()
  d()

}

결과는 Promise의 then메서드를 사용했을 때와 똑같다. 하지만 test함수의 내부가 훨씬 직관적이고 깔끔하다. async는 비동기 실행을 할 함수를 나타내고 await는 해당 함수가 실행이 완료될 때까지 대기한 뒤에, 다음 코드를 실행한다는 의미이다.


3.2 예외처리

function a(number) {
  // resolve가 실행될 수 없을 때 reject
  return new Promise((resolve, reject) => {
    if (number > 4) {
      reject()
      return
    }
    setTimeout(() => {
      console.log('A')
      resolve()
    }, 1000)
  })
}

async function test() {
  try {
    await a(11)
    console.log('resolve')
  } catch (err) {
    console.log('reject')
  } finally {
    console.log('done')
  }
}

0개의 댓글