Promise() 构造函数

Promise() 构造函数创建 Promise 对象。它主要用于封装尚不支持 Promise 的基于回调的 API。

¥The Promise() constructor creates Promise objects. It is primarily used to wrap callback-based APIs that do not already support promises.

Try it

语法

¥Syntax

js
new Promise(executor)

注意:Promise() 只能与 new 一起构建。尝试在没有 new 的情况下调用它会抛出 TypeError

¥Note: Promise() can only be constructed with new. Attempting to call it without new throws a TypeError.

参数

¥Parameters

executor

由构造函数执行的 function。它接收两个函数作为参数:resolveFuncrejectFuncexecutor 中抛出的任何错误都会导致 Promise 被拒绝,并且返回值将被忽略。executor 的语义详述如下。

返回值

¥Return value

当通过 new 调用时,Promise 构造函数返回一个 Promise 对象。当调用函数 resolveFuncrejectFunc 时,promise 对象将被解析。请注意,如果调用 resolveFuncrejectFunc 并传递另一个 Promise 对象作为参数,则可以说是 "resolved",但仍然不是 "settled"。请参阅 承诺说明 了解更多说明。

¥When called via new, the Promise constructor returns a promise object. The promise object will become resolved when either of the functions resolveFunc or rejectFunc are invoked. Note that if you call resolveFunc or rejectFunc and pass another Promise object as an argument, it can be said to be "resolved", but still not "settled". See the Promise description for more explanation.

描述

¥Description

传统上(在 Promise 之前),异步任务被设计为回调。

¥Traditionally (before promises), asynchronous tasks were designed as callbacks.

js
readFile("./data.txt", (error, result) => {
  // This callback will be called when the task is done, with the
  // final `error` or `result`. Any operation dependent on the
  // result must be defined within this callback.
});
// Code here is immediately executed after the `readFile` request
// is fired. It does not wait for the callback to be called, hence
// making `readFile` "asynchronous".

为了利用 Promise 提供的可读性改进和语言功能,Promise() 构造函数允许将基于回调的 API 转换为基于 Promise 的 API。

¥To take advantage of the readability improvement and language features offered by promises, the Promise() constructor allows one to transform the callback-based API to a promise-based one.

注意:如果你的任务已经是基于承诺的,你可能不需要 Promise() 构造函数。

¥Note: If your task is already promise-based, you likely do not need the Promise() constructor.

executor 是自定义代码,它将回调中的结果与承诺联系起来。你,程序员,编写 executor。其签名预计为:

¥The executor is custom code that ties an outcome in a callback to a promise. You, the programmer, write the executor. Its signature is expected to be:

js
function executor(resolveFunc, rejectFunc) {
  // Typically, some asynchronous operation that accepts a callback,
  // like the `readFile` function above
}

resolveFuncrejectFunc 也是函数,你可以给它们任何你想要的实际名称。他们的签名很简单:它们接受任何类型的单个参数。

¥resolveFunc and rejectFunc are also functions, and you can give them whatever actual names you want. Their signatures are simple: they accept a single parameter of any type.

js
resolveFunc(value); // call on resolved
rejectFunc(reason); // call on rejected

传递给 resolveFuncvalue 参数可以是另一个 Promise 对象,在这种情况下,新构造的 Promise 的状态将为传递的 Promise 的 "锁定"(作为 resolution Promise 的一部分)。rejectFunc 的语义与 throw 语句接近,因此 reason 通常是 Error 实例。如果省略 valuereason,则承诺将通过 undefined 履行/拒绝。

¥The value parameter passed to resolveFunc can be another promise object, in which case the newly constructed promise's state will be "locked in" to the promise passed (as part of the resolution promise). The rejectFunc has semantics close to the throw statement, so reason is typically an Error instance. If either value or reason is omitted, the promise is fulfilled/rejected with undefined.

executor 的完成状态对 Promise 状态的影响有限:

¥The executor's completion state has limited effect on the promise's state:

  • executor 返回值被忽略。executor 中的 return 语句仅影响控制流并改变函数的一部分是否被执行,但对承诺的履行值没有任何影响。如果 executor 退出并且将来不可能调用 resolveFuncrejectFunc(例如,没有安排异步任务),则 Promise 将永远处于待处理状态。
  • 如果 executor 中抛出错误,则承诺将被拒绝,除非 resolveFuncrejectFunc 已被调用。

注意:未决承诺的存在不会阻止程序退出。如果事件循环为空,则程序将退出,尽管有任何待处理的承诺(因为这些承诺必然是永远待处理的)。

¥Note: The existence of pending promises does not prevent the program from exiting. If the event loop is empty, the program exits despite any pending promises (because those are necessarily forever-pending).

以下是典型流程的摘要:

¥Here's a summary of the typical flow:

  1. 构造函数在生成新的 Promise 对象的同时,还生成了一对对应的 resolveFuncrejectFunc 函数;这些是 "tethered" 到 Promise 对象。
  2. executor 通常封装一些异步操作,提供基于回调的 API。回调(传递给原始基于回调的 API 的回调)是在 executor 代码中定义的,因此它可以访问 resolveFuncrejectFunc
  3. executorresolveFuncrejectFunc 函数作为参数同步调用(当 Promise 构造完成后)。
  4. executor 内的代码有机会执行一些操作。异步任务的最终完成通过 resolveFuncrejectFunc 引起的副作用与 Promise 实例进行通信。副作用是 Promise 对象变成了 "resolved"。
    • 如果先调用 resolveFunc,则传递的值将为 resolved。Promise 可能会保持挂起状态(如果另一个 thenable 被传递)、被履行(在大多数情况下,传递了一个不可实现的值)或被拒绝(如果解析值无效)。
    • 如果先调用 rejectFunc,则 Promise 立即被拒绝。
    • 一旦调用其中一个解析函数(resolveFuncrejectFunc),promise 就会保持解析状态。只有对 resolveFuncrejectFunc 的第一次调用会影响 Promise 的最终状态,而对任一函数的后续调用既不能更改履行值/拒绝原因,也不能将其最终状态从 "fulfilled" 切换到 "rejected" 或相反。
    • 如果 executor 通过抛出错误退出,则承诺将被拒绝。但是,如果已调用解析函数之一(以便已解决 Promise),则该错误将被忽略。
    • 解决承诺并不一定会导致承诺被履行或拒绝(即解决)。Promise 可能仍处于待处理状态,因为它已通过另一个 thenable 解决,但其最终状态将与已解决的 thenable 的状态相匹配。
  5. 一旦 Promise 确定,它就会(异步)调用通过 then()catch()finally() 关联的任何其他处理程序。最终的履行值或拒绝原因作为输入参数传递给履行和拒绝处理程序的调用(参见 连锁承诺)。

例如,上面基于回调的 readFile API 可以转换为基于 Promise 的 API。

¥For example, the callback-based readFile API above can be transformed into a promise-based one.

js
const readFilePromise = (path) =>
  new Promise((resolve, reject) => {
    readFile(path, (error, result) => {
      if (error) {
        reject(error);
      } else {
        resolve(result);
      }
    });
  });

readFilePromise("./data.txt")
  .then((result) => console.log(result))
  .catch((error) => console.error("Failed to read data"));

resolvereject 回调仅在执行器函数的范围内可用,这意味着在构造 Promise 后你无法访问它们。如果你想在决定如何解决之前构建 Promise,则可以使用 Promise.withResolvers() 方法,该方法公开 resolvereject 函数。

¥The resolve and reject callbacks are only available within the scope of the executor function, which means you can't access them after the promise is constructed. If you want to construct the promise before deciding how to resolve it, you can use the Promise.withResolvers() method instead, which exposes the resolve and reject functions.

解析函数

¥The resolve function

resolve 函数具有以下行为:

¥The resolve function has the following behaviors:

  • 如果使用与新创建的 Promise 相同的值来调用它(Promise 是 "拴在"),则 Promise 将被拒绝并显示 TypeError
  • 如果使用非 thenable 值(基元或 then 属性不可调用的对象,包括该属性不存在时)调用它,则立即使用该值实现承诺。
  • 如果使用 thenable 值(包括另一个 Promise 实例)调用它,则 thenable 的 then 方法将被保存并在将来调用(它总是异步调用)。then 方法将通过两个回调进行调用,这两个回调是两个新函数,其行为与传递给 executor 函数的 resolveFuncrejectFunc 完全相同。如果调用 then 方法抛出异常,则当前的 Promise 将被拒绝并抛出错误。

在最后一种情况下,它意味着如下代码:

¥In the last case, it means code like:

js
new Promise((resolve, reject) => {
  resolve(thenable);
});

大致相当于:

¥Is roughly equivalent to:

js
new Promise((resolve, reject) => {
  try {
    thenable.then(
      (value) => resolve(value),
      (reason) => reject(reason),
    );
  } catch (e) {
    reject(e);
  }
});

除了 resolve(thenable) 案例之外:

¥Except that in the resolve(thenable) case:

  1. resolve 是同步调用的,因此即使尚未调用通过 anotherPromise.then() 附加的处理程序,再次调用 resolvereject 也不会产生任何效果。
  2. then 方法是异步调用的,因此如果传递了 thenable,则 Promise 永远不会立即得到解决。

因为 resolve 被再次调用,无论 thenable.then() 作为 value 传递给它,解析器函数能够展平嵌套的 thenable,其中一个 thenable 用另一个 thenable 调用它的 onFulfilled 处理程序。结果是,真实 Promise 的履行处理程序永远不会收到 thenable 作为其履行值。

¥Because resolve is called again with whatever thenable.then() passes to it as value, the resolver function is able to flatten nested thenables, where a thenable calls its onFulfilled handler with another thenable. The effect is that the fulfillment handler of a real promise will never receive a thenable as its fulfillment value.

示例

¥Examples

将基于回调的 API 转变为基于 Promise 的 API

¥Turning a callback-based API into a promise-based one

要为函数提供 Promise 功能,请让它通过在正确的时间调用 resolvereject 函数来返回 Promise。

¥To provide a function with promise functionality, have it return a promise by calling the resolve and reject functions at the correct times.

js
function myAsyncFunction(url) {
  return new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest();
    xhr.open("GET", url);
    xhr.onload = () => resolve(xhr.responseText);
    xhr.onerror = () => reject(xhr.statusText);
    xhr.send();
  });
}

调用 resolveFunc 的效果

¥Effect of calling resolveFunc

调用 resolveFunc 会导致 Promise 被解析,因此再次调用 resolveFuncrejectFunc 不会产生任何效果。但是,该承诺可能位于以下任何州:待定、已完成或已拒绝。

¥Calling resolveFunc causes the promise to become resolved, so that calling resolveFunc or rejectFunc again has no effect. However, the promise may be in any of the states: pending, fulfilled, or rejected.

这个 pendingResolved Promise 在它创建的时候就被解决了,因为它已经是 "锁定" 来匹配内部 Promise 的最终状态,并且调用 resolveOuterrejectOuter 或者稍后在执行器中抛出错误对其最终状态没有影响。然而,内部 Promise 直到 100 毫秒后仍然处于等待状态,因此外部 Promise 也处于等待状态:

¥This pendingResolved promise is resolved the time it's created, because it has already been "locked in" to match the eventual state of the inner promise, and calling resolveOuter or rejectOuter or throwing an error later in the executor has no effect on its eventual state. However, the inner promise is still pending until 100ms later, so the outer promise is also pending:

js
const pendingResolved = new Promise((resolveOuter, rejectOuter) => {
  resolveOuter(
    new Promise((resolveInner) => {
      setTimeout(() => {
        resolveInner("inner");
      }, 100);
    }),
  );
});

这个 fulfilledResolved 承诺在它被解决的那一刻就得到了履行,因为它是用一个非 thenable 值来解决的。然而,当它被创建时,它是未解决的,因为 resolvereject 都还没有被调用。未解决的承诺必然是悬而未决的:

¥This fulfilledResolved promise becomes fulfilled the moment it's resolved, because it's resolved with a non-thenable value. However, when it's created, it's unresolved, because neither resolve nor reject has been called yet. An unresolved promise is necessarily pending:

js
const fulfilledResolved = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("outer");
  }, 100);
});

调用 rejectFunc 显然会导致承诺被拒绝。然而,还有两种方法可以导致即使调用了 resolveFunc 回调,promise 也会立即被拒绝。

¥Calling rejectFunc obviously causes the promise to reject. However, there are also two ways to cause the promise to instantly become rejected even when the resolveFunc callback is called.

js
// 1. Resolving with the promise itself
const rejectedResolved1 = new Promise((resolve) => {
  // Note: resolve has to be called asynchronously,
  // so that the rejectedResolved1 variable is initialized
  setTimeout(() => resolve(rejectedResolved1)); // TypeError: Chaining cycle detected for promise #<Promise>
});

// 2. Resolving with an object which throws when accessing the `then` property
const rejectedResolved2 = new Promise((resolve) => {
  resolve({
    get then() {
      throw new Error("Can't get then property");
    },
  });
});

规范

Specification
ECMAScript Language Specification
# sec-promise-constructor

¥Specifications

浏览器兼容性

BCD tables only load in the browser

¥Browser compatibility

也可以看看