Promise

Synchronous 동기

  • 요청을 보낸 후 해당 요청의 응답을 받아야 다음 동작을 실행 (은행)

Asynchronous 비동기

  • 요청을 보낸 후 응답과 관계없이 다음 동작을 실행 (카페)


Intro

자바스크립트는 동기적이며, single threaded이다

  • 자바스크립트는 single threaded(프로세스 내에서 단일 실행, 콜스택이 하나)로서, 여러 개의 스크립트가 동시에 실행될 수 없다. => 한 번에 하나의 명령만 처리
  • 브라우저에서 자바스크립트는 사용자 작업의 처리, 스타일 업데이트 등의 다른 작업과 같은 대기열에 있으며, 브라우저는 어떠한 작업이 완료되기 전에 다음 작업을 수행할 수 없다. => blocking이 일어난다
  • 자바스크립트는 동기적이며 각 코드가 순서대로 실행된다.


자바스크립트의 비동기 프로그래밍

  • 자바스크립트는 비동기 프로그래밍을 사용하여 요청을 보낸 후 응답에 관계 없이 바로 다음 동작을 이어서 실행한다.
  • 자바스크립트의 비동기 함수는 특정 코드의 실행이 끝나기 전에 계속 다음 코드를 실행하기 때문에, 실행이 서로 연관되어 있는 코드가 있을 경우 에러가 발생할 수 있다.
  • 주로 서버에서 받아온 데이터를 화면에 표시할 때 사용하는데, 서버에 데이터 요청 후 화면에 표시될 때 데이터를 다 받아오지 않은 상태에서는 오류가 발생하거나 빈 화면이 표시되게 된다. 때문에 데이터를 받아오는 작업이 모두 완료된 후 화면에 표시하는 함수가 실행되어야 하는데, 이러한 비동기 처리 방식의 문제를 해결하기 위해 콜백 함수를 사용한다.
    • 콜백 함수: 자바스크립트에서 비동기를 관리하는 방법 중 하나
    • 콜백 함수를 parameter로 받게 되면 제어권을 콜백 함수에게 넘겨주게 된다.
    • 즉, 콜백 함수는 비동기 함수가 완료된 후 실행되어야 할 코드를 담고 있다가 함수 내에서 정해진 방식에 따라 콜백 함수를 호출할 때 실행된다.
    • 비동기를 순서대로 처리하기 위해 콜백 함수를 중첩하여 사용하다 보면 depth가 깊어져 콜백 지옥이 생길 수 있다.
 // 콜백 지옥
 step1(function (value1) {
    step2(function (value2) {
        step3(function (value3) {
            step4(function (value4) {
                step5(function (value5) {
                    step6(function (value6) {
                        // Do something with value6
                    });
                });
            });
        });
    });
});
  • 이러한 콜백 지옥을 해결하기 위해 Promise, AsyncAwait를 사용한다.
  • 비동기적인 함수를 바로 실행하는 대신 동기 함수처럼 값을 반환하도록 한다.


Promise

  • 비동기 처리에 사용되는 객체
  • 비동기 작업이 완료된 후의 값을 객체로 받아 이후 처리를 관리한다.
    • 최종 결과가 아니라 프로미스를 반환하여 미래의 어떤 시점에 결과를 제공한다.
  • new Promise를 통해 인스턴스를 생성한다.
  • 매개변수로 executor(실행 함수)를 전달받고, 이 함수가 프로미스 구현에 의해 다시 resolve/reject 함수를 인자로 받는다.


Promise의 상태

  • 대기(pending): 이행하거나 거부되지 않은 초기 상태
  • 이행(fulfilled): 비동기 작업이 성공적으로 완료되어 resolve가 호출된 상태
  • 거부(rejected): 비동기 작업 중 오류가 발생하여 reject를 호출한 상태


Promise의 메소드

  • resolve(): 비동기 작업 성공 시 주어진 결과값을 담은 Promise 객체를 반환 => 이행 상태
  • reject(): 비동기 작업 실패 시 에러 값(오류 원인)을 담은 Promise 객체를 반환 => 거부 상태
const promise1 = new Promise((resolve, reject) => {
 setTimeout(() => {
  resolve('do');
 }, 300);
});

promise1.then((value) => console.log(value)); // 결과값: 'do'
console.log(promise1) // 결과값: [object Promise]


Promise 프로토타입 메소드

  • then(): resolve를 통해 받은 값을 전달받아 Promise 객체를 반환
    • promise가 이행 상태일 때를 위한 콜백함수와 거부 상태일 때를 위한 콜백함수를 인자로 받으며, 인자는 생략 가능하고 생략 시 추가 핸들러가 없는 Promise를 생성하게 된다.
    • 이행 상태 콜백함수(onFulfilled)는 이행 값을 인자로 받고, 거부 상태 콜백함수(onRejected)는 거부 이유를 인자로 받는다.
// 방법1
promise.then(onFulfilled, onRejected);

// 방법2
promise.then(function(value) {
 // 이행
}, function(reason) {
 // 거부
});


  • catch(): reject를 통해 받은 값을 전달받아 Promise 객체를 반환
    • catch(onRejected) === then(null, onRejected)
// 방법1
promise.catch(onRejected);

// 방법2
promise.catch(function(reason) {
 // 거부
});


Promise Chaining

  • 순차적으로 각각의 작업이 이전 단계 비동기 작업이 성공하고 나서 그 결과값을 이용하여 다음 비동기 작업을 실행할 때 chain을 사용할 수 있다.
  • then() 메소드는 처음 promise와는 다른 새로운 promise를 반환한다.
const promise1 = doSomething();
const promise2 = promise1.then(successCallback, failureCallback);
//
const promise2 = doSomething().then(successCallback, failureCallback);
  • 여러개의 콜백을 추가하여 각각의 콜백이 주어진 순서대로 실행되게 된다.
doSomething()
.then(data1 => doSomethingElse(data1))
.then(data2 => doThirdThing(data2))
.then(data3 => {
  console.log(data3))
})
.catch(failureCallback);


Async/await

  • 비동기를 관리하는 또 다른 방법으로, AsyncFunction 객체를 반환하는 비동기 함수(async function)를 정의한다.
  • async 키워드를 통해 비동기 함수를 선언한다. async 함수는 항상 promise를 반환한다.
  • Promise를 가독성 좋게 사용하는 방법이다.
  • await 키워드는 함수의 실행을 일시 중지하고 전달된 프로미스가 처리(settled)될 때까지 기다린 다음 async 함수의 실행을 다시 시작하고 완료 후 값을 반환하게 한다.
  • await 키워드를 포함하는 최상위 코드는 동기적으로 실행되고, aync 함수는 비동기적으로 완료된다.
  • await 키워드는 async 함수 내에서만 유효하며, await 키워드가 없는 async 함수는 동기적으로 실행된다.

Error handling

  • try…catch문을 사용한다.
  • error가 발생할 경우 catch block 실행
const fetchAsync = async() => {
 try {
  const response = await fetch(URL);
  const user = await response.json();
 } catch (err) {
   alert(err);
 }
}


Promise.all

  • 여러 프로미스를 한번에 배열로 받아 모든 프로미스가 성공적으로 완료된 후에 Promise를 반환한다.
  • 프로미스 중 하나라도 거부되는 경우 거부된 프로미스의 에러 이유를 그대로 사용하여 거부한다.
Promise.all([promise1, promise2, promise3]).then((values => {
 console.log(values);
}];
  • async/await와 Promise.all
  • Promise.all을 사용하면 병렬로 비동기 함수를 실행시킬 수 있다.
    • 더 빠른 처리가 가능하고, 중간에 비동기 처리가 실패했을 시에도 즉시 동작을 종료하고 에러를 반환한다.
  • 순서대로 실행되지만 앞의 함수가 완료되는 것을 기다리지 않고 다음 함수가 실행되고, 마지막으로 완료되는 함수를 기다렸다가 값을 반환한다.
// async/await
await display('data1', 3000)
await display('data2', 2000)
await display('data3', 1000)
// 결과: (3000ms) -> data1 -> (2000ms) -> data2 -> (1000ms) -> data1
// 소요시간: 약 6000ms

// Promise.all 병렬 실행
await Promise.all([
 display('data1', 3000),
 display('data2', 2000),
 display('data3', 1000)
]);
// 결과: data1, data2, data1
// 소요시간: 약 3000ms 안에 모두 실행

Categories:

JavaScript

Load Comments