Js - Promise

박요셉·2024년 5월 11일
3

Javascript

목록 보기
10/11

Promise 설명

Promise는 프로미스가 생성된 시점에는 알려지지 않았을 수도 있는 값을 위한 대리자로, 비동기 연산이 종료된 이후에 결과 값과 실패 사유를 처리하기 위한 처리기를 연결할 수 있다.

프라미스를 사용하면 비동기 메서드에서 맟 ㅣ동기 메서드처럼 값을 반환할 수 있습니다만 최종 결과를 반환하는 것이 아니고, 미래의 어떤 시점에 결과를 제공하겠다는프로미스(promise)를 반환합니다.

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

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

대기 중인 프로미스는 값과 함께 이행할 수도, 어떤 이유(오류)로 인해 거부될 수도 있다.
이행 또는 거부될 때, 프로미스의 then 메서드에 의해 대기열(큐)에 추가된 처리기들이 호출된다.
이미 이행했거나 거부된 프로미스에 처리기를 연결해도 호출되므로, 비동기 연산과 처리기 연결 사이의 경합 조건은 없습니다.

프로미스가 이행되거나 거부되었지만 보류 중이 아닌 경우, 프로미스가 확정된 것으로 간주한다.

또한 프로미스와 함께 resolved라는 용어가 사용되는 것을 볼 수 있는데, 이는 프로미스가 다른 프로미스의 최종 상태와 일치하도록 "settled"되거나 "locked-in"되어 더 이상 해결하거나 거부해도 아무런 효과가 없음을 의미합니다.

States and fates에 프로미스 용어에 대한 자세한 내용이 나와 있다.

States and Fates

States and Fates 문서에 대한 정리이다.

이 문서는 약속을 'state'와 'fate'이라는 두 가지 범주로 나누어 약속을 둘러싼 다양한 형용사를 명확히 설명하는 데 도움이 된다.

Overview and Operational Definitions

States

Promise에는 이행됨, 거부됨, 보류 중이라는 세 가지 상호 베타적인 상태가 있습니다.

Promise이 이행되면 Promise.then(f)가 f를 "가능한 한 빨리"라고 호출합니다.
Promise이 거부된 상태는 Promise.then(undefined,r)이 "가능한 한 빨리"r을 호출하면 Promise가 거부된 것이다.
Promise가 이행되지도 거부되지도 않으면 Promise는 보류 중이다.
Promise가 보류 중이 아닌 경우, 즉 이행되거나 거부되면 Promise가 정착되었다고 말한다.
해결되었다는 것은 언어적 편의상 상태를 말하는 것이 아니다.

Fates

Promise에는 해결된 경우와 해결되지 않은 경우의 두 가지 상호 배타적인 fate가 있다.

  • Promise를 해결하거나 거부하려고 해도 효과가 없는 경우, 즉 Promise가 다른 Promise를 따르도록 '고정(locked In)'되었거나 Promise가 이행되거나 거부된 경우 Promise는 해결된 것이다.

  • Promise가 해결되지 않은 경우, 즉 Promise를 해결하거나 거부하려고 하면 Promise에 영향을 미치는 경우 Promise는 해결되지 않은 것이다.

PromisePromise 또는 then으로 '해결(resolved)'될 수 있으며, 이 경우 나중에 언래핑을 위해 Promise 또는 then을 저장하거나, Promise가 아닌 값으로 해결될 수 있으며, 이 경우 해당 값으로 이행된다.

Relating States and Fates

'fate'가 해결된 Promise는 세 가지 상태 중 하나에 있을 수 있다.

  • Fulfilled - '비프로미스(non-promise)'값으로 해결되었거나, 통과된 모든 이행 처리기를 가능한 한 빨리 다시 호출하는(henable which will call any passed fuilfillment handler back) then으로 해결되었거나, 이행된 다른 Promise로 해결된 경우이다.
  • Rejected - 직접 거부되었거나, 전달된 거부 처리기를 가능한 한 빨리 다시 호출하거나(thenable which will call any passed rejection handler back), 다른 Promise로 resolved된 경우
  • Pending - 가능한 한 빨리 처리기를 다시 호출하지 않는 처리기로 처리되었거나 보류 중인 다른 Promise로 처리된 경우

fate가 결정되지 않은 Promise는 반드시 보류 중일 수밖에 없다.
이러한 관계는 재귀적이라는 점에 유의해야 한다.
예를 들어, 해결된 Promise가 거부된 Promise로 이행 핸들러를 호출하는 경우 그 자체로 거부된 것이다.

Relation to the Spec

Promise의 상태는 [[PromiseState]]내부 슬롯에 반영된다.
Promise의 fate는 '해결 함수(resolving functions)'의 일부로 암시적으로 저장된다.

"resolved" Promise는 종종 "fulfilled"Promise 동일하지만, "States and fates"에 설명된 것처럼 해결된 Promise도 보류 중이거나 거부될 수 있다.

다음은 예제이다.

new Promise((resolveOuter) => {
  resolveOuter(
    new Promise((resolveInner) => {
      setTimeout(resolveInner, 1000);
    }),
  );
});

Promise는 생성될 때 resolveOuter가 동기적으로 호출되기 때문에 이미 해결되었지만, 다른 Promise와 함께 해결되므로 1초 후 내부 Promise가 이행될 때까지는 이행되지 않는다.
실제로 "resolution"은 종종 백그라운드에서 이루어지며 관찰할 수 없으며, 이행 또는 거부 여부만 확인할 수 있습니다.

참고: 느긋한 평가와 연산 연기를 위한 방법을 프로미스라고 부르는 언어도 여럿 존재합니다(Scheme 등). JavaScript에서의 프로미스는 콜백 함수를 연결할 수 있는, 이미 진행 중인 프로세스를 나타냅니다. 표현식을 느긋하게 평가하려면 f = () => expression처럼 매개변수 없는 함수를 사용해 느긋한 표현식을 생성하고, f()를 호출해 평가하세요.

프로미스 연결

Promise.prototype.then(), Promise.prototype.catch()Promise.prototype.finally()메서드는 정착된 Promise와 추가 작업을 연결하는 데 사용된다.
이러한 메서드는 Promise를 반환하므로 연쇄적으로 연결할 수 있다.

.then()메서드는 최대 두 개의 인수를 받습니다. 첫 번째 인수는 Promise의 이행된 경우에 대한 콜백 함수이고, 두 번째 인수는 거부된 경우에 대한 콜백 함수입니다. 각 .then()은 새로 생성된 프로미스 객체를 반환하며, 선택적으로 연쇄에 사용할 수 있다.

다음은 예제이다.

const myPromise = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("foo");
  }, 300);
});

myPromise
  .then(handleFulfilledA, handleRejectedA)
  .then(handleFulfilledB, handleRejectedB)
  .then(handleFulfilledC, handleRejectedC);

.then()Promise 객체를 반환하는 콜백 함수가 없는 경우에도 처리는 체인의 다음 링크까지 계속된다. 따라서 체인은 마지막 .catch()까지 모든 거부 콜백 함수를 안전하게 생략할 수 있다.

.then()에서 거부된 Promise를 처리하면 Promise 페인의 더 아래쪽에 영향을 미친다.
오류를 즉시 처리해야 하기 때문에 선택의 여지가 없는 경우도 있습니다.
이러한 경우 체인에서 오류 상태를 유지하기 위해 어떤 유형의 오류를 발생시켜야 한다.
반면에 즉각적인 필요성이 없는 경우에는 마지막 .catch() 문까지 오류 처리를 생략하는 것이 더 간단하다.
.catch()는 실제로는 약속이 이행된 경우에 대한 콜백 함수를 위한 슬롯이 없는 .then()에 불과하다.

myPromise
  .then(handleFulfilledA)
  .then(handleFulfilledB)
  .then(handleFulfilledC)
  .catch(handleRejectedAny);

콜백 함수에 화살표 함수를 사용하면 Promise 체인의 구현은 다음과 같다.

myPromise
  .then((value) => `${value} and bar`)
  .then((value) => `${value} and bar again`)
  .then((value) => `${value} and again`)
  .then((value) => `${value} and again`)
  .then((value) => {
    console.log(value);
  })
  .catch((err) => {
    console.error(err);
  });

참고: 실행 속도를 높이려면 모든 동기 작업을 하나의 핸들러 내에서 수행하는 것이 바람직하며, 그렇지 않으면 모든 핸들러를 순차적으로 실행하는 데 몇 틱이 걸립니다.

Promise의 종료 조건에 따라 체인에서 다음 Promise의 "settled" 상태가 결정된다.
"fullfilled" 상태는 Promise가 성공적으로 완료 되었음을 나타내며, "rejected" 상태는 성공하지 못했음을 나타낸다.

체인에서 이행된 각 Promise의 반환 값은 다음 .then()으로 전달되고, 거부 사유의 체인의 다음 거절 핸들러 함수로 전달된다.

체인의 약속은 서로 중첩되어 있지만 스택의 맨 꼭대기처럼 튀어나온다.
체인의 첫 번째 Promise는 가장 깊게 중첩되어 있으며 가장 먼저 나오게 된다.
(promise D, (promise C, (promise B, (promise A) ) ) )

nextValuePromise인 경우, 그 결과는 동적으로 대체된다.
returnPromise를 빠져나가게 하지만 nextValue Promise는 그 자리에 밀어넣게 된다.
위에 표시된 중첩에서 "promise B"와 관련된 .then()이 "promise X"의 nextValue를 반환하는 경우, 결과 중첩 구조는 다음과 같이 보일 것이다.
(promise D, (promise C, (promise X) ) )

하나의 Promise는 둘 이상의 중첩에 참여할 수 있다.
다음 코드의 경우, promiseA를 "settled" 상태로 전환하면 .then()의 두 인스턴스가 모두 호출된다.

const promiseA = new Promise(myExecutorFunc);
const promiseB = promiseA.then(handleFulfilled1, handleRejected1);
const promiseC = promiseA.then(handleFulfilled2, handleRejected2);

이미 "settled"된 Promise에 동작을 할당할 수 있습니다. 이 결우, 해당 동작이 적절하다면 첫 번째 비동기 기회에 수행됩니다.
Promise는 비동기적으로 실행되는 것이 보장되므로, 이미 "settled"된 Promise에 대한 동작은 스택이 해제되고 시간이 경과한 후에만 발생한다.
이 효과는 setTimeout(action,0)과 유사하다.

const promiseA = new Promise((resolve, reject) => {
  resolve(777);
});
// At this point, "promiseA" is already settled.
promiseA.then((val) => console.log("asynchronous logging has val:", val));
console.log("immediate logging");

// 이 순서로 출력을 생성합니다.
// immediate logging
// asynchronous logging has val: 777

Thenables

JavaScript 생태계는 Promise가 언어의 일부가 되기 훨씬 전부터 여러 가지 Promise 구현을 만들어왔다.
내부적으로 다르게 표현되기는 하지만, 최소한 모든 Promise와 유사한 객체는 'Thenable' 인터페이스를 구현한다.
'thenable'은 두 개의 콜백(하나는 Promise가 이행될 때, 다른 하나는 거부될 때)과 함께 호출되는 .then()메서드를 구현한다.
Promise 또한 'thenable'이다.

기존 Promise 구현과 상호 운용하기 위해 언어에서는 Promise 대신 'thenables'를 사용할 수 있다.
예를 들어 Promise.resolvePromise를 해결할 뿐만 아니라 'thenables'도 추적한다.

const aThenable = {
  then(onFulfilled, onRejected) {
    onFulfilled({
      // thenable은 다른 thenable로 채워집니다.
      then(onFulfilled, onRejected) {
        onFulfilled(42);
      },
    });
  },
};

Promise.resolve(aThenable); // 프로미스는 42로 채워집니다.

Promise 동시성

Promise 클래스는 비동기 작업 동시성을 용이하게 하기 위해 네 가지 정적 메서드를 제공한다.

  • Promise.all() : 모든 Promise가 이행되면 이행되고, Promise하나라도 거부되면 거부됩니다.

  • Promise.allSettled() : 모든 Promise가 해결되면 이행됩니다.

  • Promise.any() : Promise하나라도 이행되면 이행되고, 모든 Promise가 거부되면 거부합니다.

  • Promise.race() : Promise하나라도 해결되면 해결된다. 즉, Promise 중 하나라도 이행되면 이행되고, 약속 중 하나라도 거부되면 거부된다.

이 모든 메서드는 Promise의 'iterable(정확히 말하면,thenables)'를 취하고 새로운 Promise를 반환한다.
이 메서드들은 모두 서브클래싱을 지원하므로 Promise의 서브클래스에서 호출할 수 있으며, 그 결과는 서브클래스 유형의 Promise가 된다.
이렇게 하려면 서브클래스의 생성자가 Promise() 생성자와 동일한 시그니처를 구현해야 하며, resolvereject콜백을 매개변수로 사용하여 호출할 수 있는 executor함수를 수락해야 한다.
또한 서브클래스에는 값을 Promise로 해결하기 위해 Promise.resolve()처럼 호출할 수 있는 resolve정적 메서드가 있어야 합니다.

JavaScript는 본질적으로 'single-threaded'이므로 특정 순간에는 하나의 작업만 실행되지만, 제어권이 다른 Promise 간에 이동하여 Promise가 동시에 실행되는 것처럼 보이게 할 수 있다는 점에 유의해야 한다.
JavaScript에서 병렬 실행은 'worker threads'를 통해서만 가능하다.

생성자

Promise()
새로운 Promise 객체를 생성합니다. 주로 Promise를 지원하지 않는 함수를 감쌀 때 사용합니다.

정적 속성

Promise[@@species]
프로미스 메서드에서 반환값을 구성하는 데 사용되는 생성자를 반환한다.

정적 메서드

  • Promise.all(iterable)

    주어진 모든 Promise 가 이행되거나, 한Promise가 거부될 때까지 대기하는 새로운 Promise를 반환한다.

    반환하는 Promise가 이행된다면, 매개변수로 제공한 Promise 각각의 이행 값을 모두 모아놓은 배열로 이행한다.
    배열 요소의 순서는 매개변수에 지정한 Promise의 순서를 유지한다.

    반환하는 Promise가 거부된다면, 매개변수의 Promise 중 거부된 첫 Promise의 사유를 그대로 사용한다.

  • Promise.allSettled(iterable)

    주어진 모든 Promise가 처리(이행 또는 거부)될 때까지 대기하는 새로운 Promise를 반환합니다.

    Promise.allSettled()가 반환하는 Promise는 매개변수로 제공한 모든 Promise 각각의 상태와 값(또는 거부 사유)을 모아놓은 배열로 이행한다.

  • Promise.any(iterable)

    주어진 모든 Promise 중 하나라도 이행하는 순간, 즉시 그 Promise 값으로 이행하는 새로운 Promise를 반환한다.

  • Promise.race(iterable)

    주어진 모든 Promise 중 하나라도 처리될 때까지 대기하는 Promise를 반환합니다.

    반환하는 Promise가 이행한다면, 매개변수의 Promise 중 첫 번째로 이행한 Promise의 값으로 이행한다.

    반환하는 Promise가 거부된다면, 매개변수의 Promise 중 거부된 첫 Promise의 사유를 그대로 사용한다.

  • Promise.rejcet(reason)

    주어진 사유로 거부하는 Promise 객체를 반환한다.

  • Promise.resolve()

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

    이때 지정한 값이 then 가능한 (then메서드를 가지는)값인 경우, Promise.resolve()가 반환하는 Promisethen메서드를 "다라가서" 자신의 최종 상태를 결정한다.

    그 외의 경우, 반환된 Promise는 주어진 값으로 이행한다.

    어떤 값이 Promise인지 아닌지 알 수 없는 경우, 보통 일일이 두 경우를 나눠서 처리하는 대신 Promise.resolve()로 값을 감싸서 항상 Promise가 되도록 만든 후 작업하는 것이 좋다.

인스턴스 속성

이러한 속성은 Promise.prototype에 정의되며 모든 Promise 인스턴스에 공유된다.

-Promise.prototype.constructor

인스턴스 객체를 생성한 생성자 함수이다.
Promise인스턴스의 경우 초기값은 Promise생성자이다.

  • Promise.prototype[@@toStringtag]

    @@toStringTag 속성의 초기 값은 문자열 "Promise"이다.
    이 속성은 Object.prototype.toString()에서 사용된다.

인스턴스 메서드

  • Promise.prototype.catch()

    Promise에 거부 처리기 콜백을 추가하고, 콜백이 호출될 경우 그 반환값으로 이행하며 호출되지 않을 경우, 즉 이전 Promise가 이행하는 경우 이행한 값을 그대로 사용해 이행하는 새로운 Promise를 반환합니다.
  • Promise.prototype.finally()

    Promise의 이행과 거부 여부에 상관없이 처리될 경우 항상 호출되는 처리기 콜백을 추가하고, 이행한 값 그대로 이행하는 새로운 Promise를 반환한다.
  • Promise.prototype.then()

    Promise에 이행과 거부 처리기 콜백을 추가하고, 콜백이 호출될 경우 그 반환값으로 이행하며 호출되지 않을 경우(onFulfilled,onRejected 중 상태에 맞는 콜백이 함수가 아닐 경우) 처리된 값과 상태 그대로 처리되는 새로운 Promise를 반환합니다.

예제

기본 예제

let myFirstPromise = new Promise((resolve, reject) => {
  // 우리가 수행한 비동기 작업이 성공한 경우 resolve(...)를 호출하고, 실패한 경우 reject(...)를 호출합니다.
  // 이 예제에서는 setTimeout()을 사용해 비동기 코드를 흉내냅니다.
  // 실제로는 여기서 XHR이나 HTML5 API를 사용할 것입니다.
  setTimeout(function () {
    resolve("성공!"); // 와! 문제 없음!
  }, 250);
});

myFirstPromise.then((successMessage) => {
  // successMessage는 위에서 resolve(...) 호출에 제공한 값입니다.
  // 문자열이어야 하는 법은 없지만, 위에서 문자열을 줬으니 아마 문자열일 것입니다.
  console.log("와! " + successMessage);
});

다양한 상황의 예제

// 오류 처리를 실험하기 위해 'threshold' 값은 무작위로 오류를 발생시킵니다.
const THRESHOLD_A = 8; // 0을 사용하여 오류를 보장할 수 있습니다.

function tetheredGetNumber(resolve, reject) {
  setTimeout(() => {
    const randomInt = Date.now();
    const value = randomInt % 10;
    if (value < THRESHOLD_A) {
      resolve(value);
    } else {
      reject(`Too large: ${value}`);
    }
  }, 500);
}

function determineParity(value) {
  const isOdd = value % 2 === 1;
  return { value, isOdd };
}

function troubleWithGetNumber(reason) {
  const err = new Error("Trouble getting number", { cause: reason });
  console.error(err);
  throw err;
}

function promiseGetWord(parityInfo) {
  return new Promise((resolve, reject) => {
    const { value, isOdd } = parityInfo;
    if (value >= THRESHOLD_A - 1) {
      reject(`Still too large: ${value}`);
    } else {
      parityInfo.wordEvenOdd = isOdd ? "odd" : "even";
      resolve(parityInfo);
    }
  });
}

new Promise(tetheredGetNumber)
  .then(determineParity, troubleWithGetNumber)
  .then(promiseGetWord)
  .then((info) => {
    console.log(`Got: ${info.value}, ${info.wordEvenOdd}`);
    return info;
  })
  .catch((reason) => {
    if (reason.cause) {
      console.error("Had previously handled error");
    } else {
      console.error(`Trouble with promiseGetWord(): ${reason}`);
    }
  })
  .finally((info) => console.log("All done"));

고급 예제

다음의 작은 예제는 Promise의 동작 방식을 보여준다.
testPromise()함수는 <button>을 클릭할 때마다 호출되며, window.setTimeout()을 사용해 1~3초의 무작위 간격 이후 Promise 횟수(1부터 시작하는 숫자)로 이행하는 Promise를 생성한다.
Promise() 생성자는 Promise를 만드는데 쓰인다.

Promise 이행은 p1.then()을 사용하는 이행 콜백 세트를 통해 단순히 로그에 남는다.
약간의 로그를 통해, 함수의 동기 부분이 비동기적 Promise의 완료와 어떻게 분리되어 있는지 확인할 수 있다.

짧은 시간 동안 여러 번 클릭하면 다른 Promise가 차례로 이행되는 것을 볼 수도 있다.

HTML

<button id="btn">프로미스 만들기!</button>
<div id="log"></div>

JavaScript

"use strict";
var promiseCount = 0;

function testPromise() {
  var thisPromiseCount = ++promiseCount;

  var log = document.getElementById("log");
  log.insertAdjacentHTML(
    "beforeend",
    thisPromiseCount + ") 시작 (<small>동기적 코드 시작</small>)<br/>",
  );

  // 새 프로미스 생성 - 프로미스의 생성 순서를 전달하겠다는 약속을 함 (3초 기다린 후)
  var p1 = new Promise(
    // 실행 함수는 프로미스를 이행(resolve)하거나
    // 거부(reject)할 수 있음
    function (resolve, reject) {
      log.insertAdjacentHTML(
        "beforeend",
        thisPromiseCount +
          ") 프로미스 시작 (<small>비동기적 코드 시작</small>)<br/>",
      );
      // setTimeout은 비동기적 코드를 만드는 예제에 불과
      window.setTimeout(
        function () {
          // 프로미스 이행 !
          resolve(thisPromiseCount);
        },
        Math.random() * 2000 + 1000,
      );
    },
  );

  // 프로미스를 이행했을 때 할 일은 then() 호출로 정의하고,
  // 거부됐을 때 할 일은 catch() 호출로 정의
  p1.then(
    // 이행 값 기록
    function (val) {
      log.insertAdjacentHTML(
        "beforeend",
        val + ") 프로미스 이행 (<small>비동기적 코드 종료</small>)<br/>",
      );
    },
  ).catch(
    // 거부 이유 기록
    function (reason) {
      console.log("여기서 거부된 프로미스(" + reason + ")를 처리하세요.");
    },
  );

  log.insertAdjacentHTML(
    "beforeend",
    thisPromiseCount + ") 프로미스 생성 (<small>동기적 코드 종료</small>)<br/>",
  );
}

if ("Promise" in window) {
  var btn = document.getElementById("btn");
  btn.addEventListener("click", testPromise);
} else {
  log = document.getElementById("log");
  log.innerHTML =
    "Live example not available as your browser doesn't support the <code>Promise<code> interface.";
}

XHR로 이미지 불러오기

이미지를 로드하기 위해 PromiseXMLHttpRequest를 사용하는 또 다른 간단한 예는 MDN GitHub js-examples 저장소에서 이용할 수 있다.
실제 예를 볼 수도 있다.
각 단계에 주석이 붙어있어, Promise 및 XHR 구조를 차근차근 따라갈 수 있다.

현재 설정 객체 추적

설정 객체는 JavaScript 코드가 실행될 때 추가 정보를 제공하는 환경이다.
여기에는 realm 및 모듈 맵은 물론 출처와 같은 HTML 특정 정보가 포함된다.
브라우저가 특정 사용자 코드에 어떤 설정 객체를 사용할지 알 수 있도록 현재 설정 객체를 추적합니다.

이를 더 잘 이해하기 위해 realm이 어떻게 문제가 될 수 있는지 자세히 살펴볼 수 있습니다.
realm은 대략 전역 객체로 생각할 수 있다.
realm의 독특한 점은 JavaScript 코드를 실행하는 데 필요한 모든 정보를 담고 있다는 것이다.
여기에는 array 및 error와 같은 객체가 포함된다.
각 설정 객체에는 이러한 객체의 고유한 "복사본"이 있으며 공유되지 않습니다.
이로 인해 Promise와 관련하여 예기치 않은 동작이 발생할 수 있다.
이 문제를 해결하기 위해 기존 설정 객체라는 것을 추적한다.
이는 특정 함수 호출을 담당하는 사용자 코드의 내용과 관련된 정보를 나타낸다.

이를 좀 더 설명하기 위해 문서에 삽입된 <iframe>이 호스트와 통신하는 방식을 살펴볼 수 있다.
모든 웹 API는 현재 설정 객체를 인식하므로 다음은 모든 브라우저에서 작동한다.

<!doctype html> <iframe></iframe>
<!-- 이 부분에 realm이 있습니다. -->
<script>
  // 이 부분에도 realm이 있습니다.
  const bound = frames[0].postMessage.bind(frames[0], "some data", "*");
  // 바인딩은 내장 함수이며 사용자가 없습니다.
  // 스택의 코드이며, 어떤 realm을 사용해야 할까요?
  setTimeout(bound);
  // 가장 최신을 사용하기 때문에 여전히 작동합니다.
  // realm(기존)을 스택에 추가합니다.
</script>

Promise에도 동일한 개념이 적용된다. 위의 예제를 조금 수정하면 다음과 같이 된다.

<!doctype html> <iframe></iframe>
<!-- 이 부분에 realm이 있습니다. -->
<script>
  // 이 부분에도 realm이 있습니다.
  const bound = frames[0].postMessage.bind(frames[0], "some data", "*");
  // 바인딩은 내장 함수이며 사용자가 없습니다.
  // 스택의 코드이며, 어떤 realm을 사용해야 할까요?
  Promise.resolve(undefined).then(bound);
  // 가장 최신을 사용하기 때문에 여전히 작동합니다.
  // realm(기존)을 스택에 추가합니다.
</script>

문서의 <iframe>이 게시물 메시지를 수신하도록 변경하면 기존 설정 객체의 효과를 관찰할 수 있습니다.

<!-- y.html -->
<!doctype html>
<iframe src="x.html"></iframe>
<script>
  const bound = frames[0].postMessage.bind(frames[0], "some data", "*");
  Promise.resolve(undefined).then(bound);
</script>
<!-- x.html -->
<!doctype html>
<script>
  window.addEventListener(
    "message",
    (event) => {
      document.querySelector("#text").textContent = "hello";
      // this code will only run in browsers that track the incumbent settings object
      console.log(event);
    },
    false,
  );
</script>

위 예제에서는 현재 설정 개체를 추적하는 경우에만

참고: 현재 realm 추적은 Firefox에서 완전히 구현되어 있으며, Chrome과 Safari에서는 부분적으로 구현되어 있습니다.

profile
개발자 지망생

2개의 댓글

comment-user-thumbnail
2024년 5월 11일

아닛 요셉님 저도 오늘 Promise에 대해 공부하고 글을 적었는데 어찌 이런 우연이! 저희는 운명이 아닐런지요

1개의 답글

관련 채용 정보