lang/js

Javascript Promise

C/H 2021. 1. 4. 11:24

Promise

Promise 객체는 비동기 작업이 맞이할 미래의 완료 또는 실패와 그 결과 값을 나타냅니다.

Promise는 다음 중 하나의 상태를 가집니다.

  • 대기(pending): 이행하거나 거부되지 않은 초기 상태.
  • 이행(fulfilled): 연산이 성공적으로 완료됨.
  • 거부(rejected): 연산이 실패함.

생성자

  • Promise()
    이미 프로미스를 지원하지 않는 함수를 감쌀 때 주로 사용합니다.

속성

  • Promise.length
    값이 언제나 1인 길이 속성입니다. (생성자 인수의 수)
  • Promise.prototype
    Promise 생성자의 프로토타입을 나타냅니다.

메서드

  • Promise.all(iterable)

    • iterable 내의 모든 프로미스가 이행한 뒤 이행하고, 어떤 프로미스가 거부하면 즉시 거부하는 프로미스를 반환합니다.
      반환된 프로미스가 이행하는 경우 iterable 내의 프로미스가 결정한 값을 모은 배열이 이행 값입니다.
      반환된 프로미스가 거부하는 경우 iterable 내의 거부한 프로미스의 이유를 그대로 사용합니다.
      이 메서드는 여러 프로미스의 결과를 모을 때 유용합니다.
    • Promise.all() 메서드는 순회 가능한 객체에 주어진 모든 프로미스가 이행한 후,
      혹은 프로미스가 주어지지 않았을 때 이행하는 Promise를 반환합니다.
      주어진 프로미스 중 하나가 거부하는 경우, 첫 번째로 거절한 프로미스의 이유를 사용해 자신도 거부합니다.
    const promise1 = Promise.resolve(3);
    const promise2 = 42;
    const promise3 = new Promise((resolve, reject) => {
    setTimeout(resolve, 100, 'foo');
    });
    
    Promise.all(\[promise1, promise2, promise3\]).then((values) => {  
    console.log(values);  
    });  
    // expected output: Array \[3, 42, "foo"\]
  • Promise.allSettled(iterable)
    메소드는 배열이나 별도의 나열 가능한 객체를 통해 나열된 Promise모음이 모두 이행하거나
    거부했을 때에 대한 대응을 할 수 있는 Promise 객체를 반환한다.

    const promise1 = Promise.resolve(3);  
    const promise2 = new Promise((resolve, reject) => setTimeout(reject, 100, 'foo'));  
    const promises = \[promise1, promise2\];
    
    Promise.allSettled(promises).
        then((results) => results.forEach((result) => console.log(result.status)));
    
    // expected output:  
    // "fulfilled"  
    // "rejected"  
  • Promise.any(iterable)
    Promise.any ()는 Promise 객체의 이터 러블을 취하고,
    이터러블의 약속 중 하나가 충족되는 즉시 해당 약속의 값으로 해결되는 단일 약속을 반환합니다.
    반복 가능한 이행에 약속이없는 경우 (주어진 모든 약속이 거부 된 경우) 반환 된 약속은
    개별 오류를 그룹화하는 오류의 새 하위 클래스 인 AggregateError와 함께 거부됩니다.
    기본적으로이 메서드는 Promise.all ()과 반대입니다.

    const promise1 = Promise.reject(0);
    const promise2 = new Promise((resolve) => setTimeout(resolve, 100, 'quick'));
    const promise3 = new Promise((resolve) => setTimeout(resolve, 500, 'slow'));
    
    const promises = [promise1, promise2, promise3];
    
    Promise.any(promises).then((value) => console.log(value));
    
    // expected output: "quick"
  • Promise.race(iterable)

    • iterable 내의 어떤 프로미스가 이행하거나 거부하는 즉시 스스로 이행하거나 거부하는 프로미스를 반환합니다.

    • 이행 값이나 거부 이유는 원 프로미스의 값이나 이유를 그대로 사용합니다.

    • 이 프로미스 객체는 iterable 안에 있는 프로미스 중에 가장 먼저 완료된 것의 결과값으로 그대로 이행하거나 거부합니다.

      const promise1 = new Promise((resolve, reject) => {
      setTimeout(resolve, 500, 'one');
      });
      
      const promise2 = new Promise((resolve, reject) => {
          setTimeout(resolve, 100, 'two');
      });
      
      Promise.race(\[promise1, promise2\]).then((value) => {
          console.log(value);
          // Both resolve, but promise2 is faster
      });  
      // expected output: "two"
    • race 함수는 인자로 주어진 iterable의 프로미스 중 가장 먼저 완료(settle)되는 것과 같은 방식으로 완료(이행/거부)되고, 같은 결과값을 전달하는 Promise를 반환합니다.

    • 전달받은 iterable이 비어 있을 경우, 반환한 프로미스는 영원히 대기 상태가 됩니다.

    • Iterable에 프로미스가 아닌 값이나 이미 완료된 프로미스가 포함되어 있을 경우, Promise.race는 전달받은 iterable에서 처음으로 등장하는 이러한 값을 결과값으로 이행합니다.

    var p1 = new Promise(function(resolve, reject) {
        setTimeout(() => resolve('하나'), 500);    
    });  
    var p2 = new Promise(function(resolve, reject) {
        setTimeout(() => resolve('둘'), 100);
    });
    
    Promise.race(\[p1, p2\])  
    .then(function(value) {
        console.log(value); // "둘"
        // 둘 다 이행하지만 p2가 더 빠르므로
    });
    
    var p3 = new Promise(function(resolve, reject) {
        setTimeout(() => resolve('셋'), 100);
    });  
    var p4 = new Promise(function(resolve, reject) {
        setTimeout(() => reject(new Error('넷')), 500);
    });
    
    Promise.race(\[p3, p4\])  
    .then(function(value) {
        console.log(value); // "셋"
        // p3이 더 빠르므로 이행함
    }, function(reason) {    
        // 실행되지 않음
    });
    
    var p5 = new Promise(function(resolve, reject) {
        setTimeout(() => resolve('다섯'), 500);
    });  
    var p6 = new Promise(function(resolve, reject) {
        setTimeout(() => reject(new Error('여섯')), 100);
    });
    
    Promise.race(\[p5, p6\])  
    .then(function(value) {
        // 실행되지 않음
    }, function(error) {
        console.log(error.message); // "여섯"
        // p6이 더 빠르므로 거부함
    });
  • Promise.reject()
    Promise.reject(reason) 메서드는 주어진 이유(reason)로 거부된 Promise 객체를 반환합니다.

    Promise.reject("Testing static reject").then(function(reason) {
      // 호출되지 않음
    }, function(reason) {
        console.log(reason); // "Testing static reject"
    });
    
    Promise.reject(new Error("fail")).then(function(error) {
        // 호출되지 않음
    }, function(error) {
        console.log(error); // Stacktrace
    });
  • Promise.resolve(value)

    • 주어진 값으로 이행하는 Promise 객체를 반환합니다.

    • 값이 then 가능한 (즉, then 메서드가 있는) 경우, 반환된 프로미스는 then 메서드를 따라가고 마지막 상태를 취합니다.

    • 그렇지 않은 경우 반환된 프로미스는 주어진 값으로 이행합니다.

    • 어떤 값이 프로미스인지 아닌지 알 수 없는 경우, Promise.resolve(value) 후 반환값을 프로미스로 처리할 수 있습니다.

    • Promise.resolve(value) 메서드는 주어진 값으로 이행하는 Promise.then 객체를 반환합니다.

    • 그 값이 프로미스인 경우, 해당 프로미스가 반환됩니다.

    • 그 값이 thenable(예, "then" 메소드 가 있음)인 경우, 반환된 프로미스는 그 thenable을 "따르며", 그 최종 상태를 취합니다.

    • 그렇지 않으면 반환된 프로미스는 그 값으로 이행합니다.

    • 이 함수는 프로미스형의 객체(무언가를 결정하는 프로미스를 결정하는 프로미스 등)의 중첩된 레이어를 단일 레이어로 펼칩니다.

      주의: 스스로를 결정하는 thenable 에서 Promise.resolve 를 호출하면 안됩니다.
      이는 무한히 중첩된 프로미스를 펼치려고하므로 무한 재귀를 유발할 것입니다.
      Angular 에서 async Pipe 를 함께 사용한 예제입니다. 자세한 내용은 여기에서 확인하세요.

    const promise1 = Promise.resolve(123);
    promise1.then((value) => {
        console.log(value);
        // expected output: 123
    });    
    • value: 이 Promise에 의해 결정되는 인수. Promise 또는 이행할 thenable 일수도 있습니다.

    • return: 주어진 값으로 이행된 Promise 또는 값이 promise 객체인 경우, 값으로 전달된 promise.

    • 정적 Promise.resolve 메소드 사용

      Promise.resolve("Success").then(function(value) {
         console.log(value); // "Success"
      }, function(value) {
         // 호출되지 않음
      });
    • 배열 이행

      var p = Promise.resolve([1,2,3]);
      p.then(function(v) {
         console.log(v[0]); // 1
      });
    • 또 다른 Promise 이행

      var original = Promise.resolve(33);
      var cast = Promise.resolve(original);
      cast.then(function(value) {
         console.log('value: ' + value);
      });
      console.log('original === cast ? ' + (original === cast));
      
      // 로그 순서:
      // original === cast ? true
      // value: 33
      // 로그의 순서가 반대인 이유는 then 핸들러가 비동기로 호출되기 때문입니다. 여기에서 then 이 동작하는 방식에 대해 확인하세요.
  • Promise.catch(onRejected)
    catch() 메서드는 Promise를 반환하고 거부 된 사례 만 처리합니다.
    Promise.prototype.then(undefined, onRejected)를 호출하는 것과 동일하게 작동합니다.
    (사실 obj.catch(onRejected)를 호출하면 내부적으로 obj.then(undefined, onRejected)를 호출합니다).
    즉, 정의되지 않은 결과 값으로 폴백하려는 경우에도 onRejected 함수를 제공해야합니다 (예 : obj.catch(() => {})).

    const promise1 = new Promise((resolve, reject) => {
     throw 'Uh-oh!';
    });
    
    promise1.catch((error) => {
     console.error(error);
    });
    // expected output: Uh-oh!
  • Promise.finally(onFinally)

    • finally() 메소드는 Promise 객체를 반환합니다.
      Promise가 처리되면 충족되거나 거부되는지 여부에 관계없이 지정된 콜백 함수가 실행됩니다.
      이것은 Promise가 성공적으로 수행 되었는지 거절되었는지에 관계없이 Promise가 처리 된 후에
      코드가 무조건 한 번은 실행되는 것을 제공합니다.

      이것은 Promise의 then()catch() 핸들러에서의 코드 중복을 피하게 합니다.

    • finally 핸들러는 onFinally 라는 지정된 함수의 Promise가 반환됩니다.

    • finally() 메서드는 결과에 관계없이 promise가 처리되면 무언가를 프로세싱 또는 정리를 수행하려는 경우에 유용합니다.

    • finally() 메서드는 .then(onFinally, onFinally) 를 호출하는 것과 매우 비슷하지만 몇 가지 차이점이 있습니다:

    • 함수를 인라인으로 만들 때, 두 번 선언해야 하지 않고 한 번만 전달하거나 그것을 위한 변수를 만들 수 있습니다.

    • finally 콜백은 어떠한 인수도 전달받지 않습니다,
      왜냐하면 promise가 이행되었는지 또는 거부되었는지를 판단할 수 없기 때문입니다.
      promise의 왜 거부되었는지 또는 이행되었을때 반환되는 값이 필요하지 않거나 제공할 필요가 없을 때 활용합니다.

    • Promise.reject(3).finally(() => {}) (약속 안 함) )는 3으로 거부됩니다.

    • Promise.resolve(2).then(() => {}, () => {})(undefined로 해결될) 와 달리, Promise.resolve(2).finally(() => {}) 는 값 2로 해결됩니다.

    • 유사하게 Promise.reject(3).then(() => {}, () => {}) (undefined로 거부될)와는 달리 Promise.reject(3).finally(() => {}) 는 값 3로 거부됩니다.
      finally 콜백에서 throw(또는 거부된 promise를 반환)하면 throw()를 호출 할 때
      지정된 거부 이유로 새롭게 만들어진 promise를 반환합니다.

let isLoading = true;

fetch(myRequest).then(function(response) {
    var contentType = response.headers.get("content-type");
    if(contentType && contentType.includes("application/json")) {
        return response.json();
    }
    throw new TypeError("Oops, we haven't got JSON!");
    })  
.then(function(json) { /\* process your JSON further \*/ })  
.catch(function(error) { console.log(error); })  
.finally(function() { isLoading = false; });
  • Promise.then(onFulfilled, onRejected)
    then() 메서드는 Promise를 리턴하고 두 개의 콜백 함수를 인수로 받습니다.
    하나는 Promise가 이행했을 때, 다른 하나는 거부했을 때를 위한 콜백 함수입니다.

    매개변수 중 하나 이상을 생략했거나 함수가 아닌 값을 전달한 경우, then은 핸들러가 없는 것이 되지만 오류를 발생하지는 않습니다.
    then 바로 이전의 Promisethen에 핸들러가 없는 상태로 완료(이행이나 거부)했을 경우, 추가 핸들러가 없는 Promise가 생성되며, 원래 Promise의 마지막 상태를 그대로 물려받습니다.

    • onFulfilled
      Promise가 수행될 때 호출되는 Function으로, 이행 값(fulfillment value) 하나를 인수로 받습니다.

    • onRejected
      Promise가 거부될 때 호출되는 Function으로, 거부 이유(rejection reason) 하나를 인수로 받습니다.

    • Promise가 이행하거나 거부했을 때, 각각에 해당하는 핸들러 함수(onFulfilledonRejected)가 비동기적으로 실행됩니다.
      핸들러 함수는 다음 규칙을 따라 실행됩니다.

      • 함수가 값을 반환할 경우, then에서 반환한 프로미스는 그 반환값을 자신의 결과값으로 하여 이행합니다.
      • 값을 반환하지 않을 경우, then에서 반환한 프로미스는 undefined를 결과값으로 하여 이행합니다.
      • 오류가 발생할 경우, then에서 반환한 프로미스는 그 오류를 자신의 결과값으로 하여 거부합니다.
      • 이미 이행한 프로미스를 반환할 경우, then에서 반환한 프로미스는 그 프로미스의 결과값을 자신의 결과값으로 하여 이행합니다.
      • 이미 거부한 프로미스를 반환할 경우, then에서 반환한 프로미스는 그 프로미스의 결과값을 자신의 결과값으로 하여 거부합니다.
      • 대기 중인 프로미스를 반환할 경우, then에서 반환한 프로미스는 그 프로미스의 이행 여부와 결과값을 따릅니다.
      • 다음 예제에서 then 메서드의 비동기성을 확인할 수 있습니다.
      // 이행한 프로미스를 받으면 'then' 블록도 바로 실행되지만,
      // 핸들러는 아래 console.log에서와 같이 비동기적으로 실행됨
      const resolvedProm = Promise.resolve(33);
      
      let thenProm = resolvedProm.then(value => {
          console.log("이 부분은 호출 스택 이후에 실행됩니다. 전달받은 값이자 반환값은 " + value + "입니다.");
          return value;
      });
      // thenProm의 값을 즉시 기록
      console.log(thenProm);
      
      // setTimeout으로 함수 실행을 호출 스택이 빌 때까지 미룰 수 있음
      setTimeout(() => {
          console.log(thenProm);
      });
      
      // 로그 출력 결과 (순서대로):
      // Promise {[[PromiseStatus]]: "pending", [[PromiseValue]]: undefined}
      // "이 부분은 호출 스택 이후에 실행됩니다. 전달받은 값이자 반환값은 33입니다."
      // Promise {[[PromiseStatus]]: "resolved", [[PromiseValue]]: 33}
    • thenPromise.prototype.catch() 메서드는 프로미스를 반환하기 때문에, 체이닝이 가능합니다.
      이를 composition(구성) 이라고도 합니다.

반응형