320x100
320x100

프라미스 체이닝

: 스크립트를 불러오는 것과 같이 순차적으로 처리해야하는 비동기 작업이 여러 개 있을 때 사용하는

  비동기 처리 방법

: 프라미스 하나에 .then을 여러 개 추가하는 것은 체이닝이 아님

new Promise(function(resolve, reject) {

  setTimeout(() => resolve(1), 1000); // (*)

}).then(function(result) { // (**)

  alert(result); // 1
  return result * 2;

}).then(function(result) { // (***)

  alert(result); // 2
  return result * 2;

}).then(function(result) {

  alert(result); // 4
  return result * 2;

});

 

 

 

 

 

프라미스 반환하기

: .then(handler)에 사용된 핸들러가 프라미스를 생성하거나 반환하는 경우

  이어지는 핸들러는 프라미스가 처리될 때까지 기다리다가 

  처리가 완료되면 그 결과를 받음

new Promise(function(resolve, reject) {

  setTimeout(() => resolve(1), 1000);

}).then(function(result) {

  alert(result); // 1

  return new Promise((resolve, reject) => { // (*)
    setTimeout(() => resolve(result * 2), 1000);
  });

}).then(function(result) { // (**)

  alert(result); // 2

  return new Promise((resolve, reject) => {
    setTimeout(() => resolve(result * 2), 1000);
  });

}).then(function(result) {

  alert(result); // 4

});

 

 

 

 

프라미스와 에러 핸들링

: 프라미스가 거부되면 제어흐름이 가장 가까운 rejection 핸들러로 넘어가기 때문에

  프라미스 체인을 사용하면 에러를 쉽게 처리할 수 있음

fetch('/article/promise-chaining/user.json')
  .then(response => response.json())
  .then(user => fetch(`https://api.github.com/users/${user.name}`))
  .then(response => response.json())
  .then(githubUser => new Promise((resolve, reject) => {
    let img = document.createElement('img');
    img.src = githubUser.avatar_url;
    img.className = "promise-avatar-example";
    document.body.append(img);

    setTimeout(() => {
      img.remove();
      resolve(githubUser);
    }, 3000);
  }))
  .catch(error => alert(error.message));

 

 

 

 

 

암시적 try catch

: 프라미스 executor와 프라미스 핸들러 코드 주위에는 암시적인 try catch가 존재

: 예외가 발생하면 암시적 try catch에서 예외를 잡고 이를 reject 처럼 다룸

new Promise((resolve, reject) => {
  throw new Error("에러 발생!");
}).catch(alert); // Error: 에러 발생!


// 똑같이 동작함

new Promise((resolve, reject) => {
  reject(new Error("에러 발생!"));
}).catch(alert); // Error: 에러 발생

: executor 주위의 암시적 try catch는 스스로 에러를 잡고 에러를 거부 상태의 프라미스로 변경 시킴

: .then 핸들러를 원하는 만큼 사용하다 마지막에 .catch 하나만 붙이면 모든 에러 처리 가능

 

 

 

 

 

다시 던지기

// 실행 순서: catch -> catch
new Promise((resolve, reject) => {

  throw new Error("에러 발생!");

}).catch(function(error) { // (*)

  if (error instanceof URIError) {
    // 에러 처리
  } else {
    alert("처리할 수 없는 에러");

    throw error; // 에러 다시 던지기
  }

}).then(function() {
  /* 여기는 실행되지 않습니다. */
}).catch(error => { // (**)

  alert(`알 수 없는 에러가 발생함: ${error}`);
  // 반환값이 없음 => 실행이 계속됨

});

 

 

 

 

 

프라미스 API

: Promise 클래스에는 5가지 정적 메서드가 있음

 

 

 

Promise.all

: 복수의 프라미스를 동시에 실행시키고 모든 프라미스가 준비될 때 까지 기다림

: 이때 프라미스중 하나라도 reject 되면 에러와 함께 reject를 반환

: 에러가 발생되면 다른 프라미스는 무시됨

let promise = Promise.all([...promises...]);
Promise.all([
  new Promise(resolve => setTimeout(() => resolve(1), 3000)), // 1
  new Promise(resolve => setTimeout(() => resolve(2), 2000)), // 2
  new Promise(resolve => setTimeout(() => resolve(3), 1000))  // 3
]).then(alert); // 프라미스 전체가 처리되면 1, 2, 3이 반환됩니다. 각 프라미스는 배열을 구성하는 요소가 됩니다.
let names = ['iliakan', 'remy', 'jeresig'];

let requests = names.map(name => fetch(`https://api.github.com/users/${name}`));

Promise.all(requests)
  .then(responses => {
    // 모든 응답이 성공적으로 이행되었습니다.
    for(let response of responses) {
      alert(`${response.url}: ${response.status}`); // 모든 url의 응답코드가 200입니다.
    }

    return responses;
  })
  // 응답 메시지가 담긴 배열을 response.json()로 매핑해, 내용을 읽습니다.
  .then(responses => Promise.all(responses.map(r => r.json())))
  // JSON 형태의 응답 메시지는 파싱 되어 배열 'users'에 저장됩니다.
  .then(users => users.forEach(user => alert(user.name)));

 

 

※ 이터러블 객체가 아닌 일반 값을 Promise.all(iterable)에 사용한 예시

Promise.all([
  new Promise((resolve, reject) => {
    setTimeout(() => resolve(1), 1000)
  }),
  2,
  3
]).then(alert); // 1, 2, 3

 

 

 

 

Promise.allSettled

: 모든 프라미스가 처리될때까지 기다리고 모든 프라미스의 결과를 배열로 반환

 

- 응답이 성공할 경우

: {status:"fulfilled", value:result}

 

- 에러가 발생한 경우

: {status:"rejected", reason:error}

let urls = [
  'https://api.github.com/users/iliakan',
  'https://api.github.com/users/remy',
  'https://no-such-url'
];

Promise.allSettled(urls.map(url => fetch(url)))
  .then(results => { // (*)
    results.forEach((result, num) => {
      if (result.status == "fulfilled") {
        alert(`${urls[num]}: ${result.value.status}`);
      }
      if (result.status == "rejected") {
        alert(`${urls[num]}: ${result.reason}`);
      }
    });
  });
  
  
  // 결과
[
  {status: 'fulfilled', value: ...응답...},
  {status: 'fulfilled', value: ...응답...},
  {status: 'rejected', reason: ...에러 객체...}
]

 

 

※ Promise.allSettled의 폴리필

if(!Promise.allSettled) {
  Promise.allSettled = function(promises) {
    return Promise.all(promises.map(p => Promise.resolve(p).then(value => ({
      status: 'fulfilled',
      value
    }), reason => ({
      status: 'rejected',
      reason
    }))));
  };
}

 

 

 

 

Promise.race

: Promise.all과 비슷하나, 가장 먼저 처리되는 프라미스의 결과를 반환

let promise = Promise.race(iterable);
Promise.race([
  new Promise((resolve, reject) => setTimeout(() => resolve(1), 1000)),
  new Promise((resolve, reject) => setTimeout(() => reject(new Error("에러 발생!")), 2000)),
  new Promise((resolve, reject) => setTimeout(() => resolve(3), 3000))
]).then(alert); // 1

 

 

 

 

Promise.resolve

: 결과값이 value인 이행 상태 프라미스를 생성

: 아래 코드와 동일한 작업 수행

let promise = new Promise(resolve => resolve(value));

: 호환성을 위해 함수가 프라미스를 반환하도록 해야할 때 사용

let cache = new Map();

function loadCached(url) {
  if (cache.has(url)) {
    return Promise.resolve(cache.get(url)); // (*)
  }

  return fetch(url)
    .then(response => response.text())
    .then(text => {
      cache.set(url,text);
      return text;
    });
}

 

 

 

 

Promise.reject

: 결과 값이 error인 reject 상태의 프라미스 생성

: 아래 코드와 동일한 작업 수행

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

 

 

 

 

프라미스화

: 콜백을 받는 함수를 프라미스를 반환하는 함수로 바꾸는 것

 

- 프라미스화 전

function loadScript(src, callback) {
  let script = document.createElement('script');
  script.src = src;

  script.onload = () => callback(null, script);
  script.onerror = () => callback(new Error(`${src}를 불러오는 도중에 에러가 발생함`));

  document.head.append(script);
}

// usage:
// loadScript('path/script.js', (err, script) => {...})

 

- 프라미스화 후

let loadScriptPromise = function(src) {
  return new Promise((resolve, reject) => {
    loadScript(src, (err, script) => {
      if (err) reject(err)
      else resolve(script);
    });
  })
}

// 사용법:
// loadScriptPromise('path/script.js').then(...)

 

 

 

 

마이크로태스크 큐

: 비동기 작업을 처리하기 위한 큐 (V8 엔진에서 사용하는 내부 큐)

: FIFO 방식의 작업 처리

: 실행할 것이 아무것도 남지 않을 때만 마이크로태스크 큐에 있는 작업이 실행됨

: 프라미스 작업이 가장 마지막에 수행 되는 이유임

 

 

 

비동기 작업을 먼저 처리하는 방법

: .then을 사용해 큐에 넣으면 됨

Promise.resolve()
  .then(() => alert("프라미스 성공!"))
  .then(() => alert("코드 종료"));

 

 

 

처리되지 못한 거부 (unhandlerejcetion)

: 마이크로태스크 큐 끝에서 프라미스 에러가 처리되지 못할 때 발생

: 프라미스 체인에 .catch를 추가해 에러를 처리하면 됨

: unhandlerejcetion은 마이크로태스트 큐의 모든 작업이 완료되었을 때 생성 됨

 

 

 

 

 

 

 

Refference

 

프라미스 체이닝

 

ko.javascript.info

 

프라미스와 에러 핸들링

 

ko.javascript.info

 

프라미스 API

 

ko.javascript.info

 

프라미스화

 

ko.javascript.info

 

마이크로태스크

 

ko.javascript.info

300x250
728x90