Promise.prototype.then()

基线 广泛可用

此功能已得到充分确立,并且可在许多设备和浏览器版本上运行。它自以下时间起在所有浏览器中均可用 2015 年 7 月.

then() 方法是 Promise 实例的方法,最多接受两个参数:Promise 的 fulfilled 和 rejected 情况下的回调函数。它将回调存储在它调用的 promise 中,并立即返回另一个 Promise 对象,允许您 链接 对其他 promise 方法的调用。

试一试

语法

js
then(onFulfilled)
then(onFulfilled, onRejected)

参数

onFulfilled

当此 promise 变成 fulfilled 时异步执行的函数。它的返回值成为 then() 返回的 promise 的 fulfilled 值。该函数使用以下参数调用

value

promise fulfilled 时的值。

如果它不是函数,则在内部将其替换为一个标识函数 ((x) => x),该函数仅将 fulfilled 值传递到前面。

onRejected 可选

当此 promise 变成 rejected 时异步执行的函数。它的返回值成为 then() 返回的 promise 的 fulfilled 值。该函数使用以下参数调用

reason

promise rejected 时的值。

如果它不是函数,则在内部将其替换为一个抛出函数 ((x) => { throw x; }),该函数抛出它收到的 rejected 原因。

返回值

立即返回一个新的 Promise。无论当前 promise 的状态如何,此新 promise 在返回时始终处于 pending 状态。

onFulfilledonRejected 处理程序之一将被执行以处理当前 promise 的 fulfilled 或 rejected。调用始终异步发生,即使当前 promise 已经 settled 也是如此。返回的 promise(称之为p)的行为取决于处理程序的执行结果,遵循一组特定的规则。如果处理程序函数

  • 返回值:p 以返回的值作为其值 fulfilled。
  • 不返回值:pundefined作为其值 fulfilled。
  • 抛出错误:p 以抛出的错误作为其值 rejected。
  • 返回一个已 fulfilled 的 promise:p 以该 promise 的值作为其值 fulfilled。
  • 返回一个已 rejected 的 promise:p 以该 promise 的值作为其值 rejected。
  • 返回另一个 pending 的 promise:p 为 pending,并在该 promise fulfilled/rejected 后立即以该 promise 的值作为其值 fulfilled/rejected。

描述

then() 方法为 Promise 的最终完成(fulfilled 或 rejected)安排回调函数。它是 promise 的基本方法:thenable 协议期望所有类似 promise 的对象都公开一个 then() 方法,并且 catch()finally() 方法都通过调用对象的 then() 方法来工作。

有关 onRejected 处理程序的更多信息,请参阅 catch() 参考。

then() 返回一个新的 promise 对象,但会修改它调用的 promise 对象,将处理程序追加到内部列表中。因此,处理程序由原始 promise 保留,并且其生命周期至少与原始 promise 的生命周期一样长。例如,以下示例最终将耗尽内存,即使返回的 promise 未被保留

js
const pendingPromise = new Promise(() => {});
while (true) {
  pendingPromise.then(doSomething);
}

如果在同一个 promise 对象上调用两次 then() 方法(而不是链接),则此 promise 对象将有两个结算处理程序对。附加到同一 promise 对象的所有处理程序始终按添加顺序调用。此外,每次调用 then() 返回的两个 promise 启动单独的链,并且不会等待彼此的结算。

沿着 then() 链出现的 Thenable 对象始终会被 解析——onFulfilled 处理程序永远不会接收 thenable 对象,并且任何处理程序返回的 thenable 始终在传递到下一个处理程序之前解析。这是因为在构建新的 promise 时,会保存由executor传递的resolvereject函数,并且当当前 promise 结算时,相应的函数将使用 fulfilled 值或 rejected 原因被调用。解析逻辑来自由 Promise() 构造函数传递的resolve函数。

then() 支持子类化,这意味着它可以在 Promise 的子类的实例上调用,结果将是子类类型的 promise。您可以通过 [Symbol.species] 属性自定义返回值的类型。

示例

使用 then() 方法

js
const p1 = new Promise((resolve, reject) => {
  resolve("Success!");
  // or
  // reject(new Error("Error!"));
});

p1.then(
  (value) => {
    console.log(value); // Success!
  },
  (reason) => {
    console.error(reason); // Error!
  },
);

将非函数作为参数

js
Promise.resolve(1).then(2).then(console.log); // 1
Promise.reject(1).then(2, 2).then(console.log, console.log); // 1

链式调用

then 方法返回一个新的 Promise,允许方法链式调用。

如果作为处理程序传递给 then 的函数返回一个 Promise,则一个等效的 Promise 将被暴露给方法链中的后续 then。下面的代码片段使用 setTimeout 函数模拟异步代码。

js
Promise.resolve("foo")
  // 1. Receive "foo", concatenate "bar" to it, and resolve that to the next then
  .then(
    (string) =>
      new Promise((resolve, reject) => {
        setTimeout(() => {
          string += "bar";
          resolve(string);
        }, 1);
      }),
  )
  // 2. receive "foobar", register a callback function to work on that string
  // and print it to the console, but not before returning the unworked on
  // string to the next then
  .then((string) => {
    setTimeout(() => {
      string += "baz";
      console.log(string); // foobarbaz
    }, 1);
    return string;
  })
  // 3. print helpful messages about how the code in this section will be run
  // before the string is actually processed by the mocked asynchronous code in the
  // previous then block.
  .then((string) => {
    console.log(
      "Last Then: oops... didn't bother to instantiate and return a promise in the prior then so the sequence may be a bit surprising",
    );

    // Note that `string` will not have the 'baz' bit of it at this point. This
    // is because we mocked that to happen asynchronously with a setTimeout function
    console.log(string); // foobar
  });

// Logs, in order:
// Last Then: oops... didn't bother to instantiate and return a promise in the prior then so the sequence may be a bit surprising
// foobar
// foobarbaz

then() 返回的值与 Promise.resolve() 的解析方式相同。这意味着支持 thenable 对象,如果返回值不是 promise,则会隐式地将其包装在一个 Promise 中,然后解析。

js
const p2 = new Promise((resolve, reject) => {
  resolve(1);
});

p2.then((value) => {
  console.log(value); // 1
  return value + 1;
}).then((value) => {
  console.log(value, "- A synchronous value works"); // 2 - A synchronous value works
});

p2.then((value) => {
  console.log(value); // 1
});

如果函数抛出错误或返回一个 rejected 的 Promise,则 then 调用返回的 promise 最终会 rejected。

js
Promise.resolve()
  .then(() => {
    // Makes .then() return a rejected promise
    throw new Error("Oh no!");
  })
  .then(
    () => {
      console.log("Not called.");
    },
    (error) => {
      console.error(`onRejected function called: ${error.message}`);
    },
  );

在实践中,通常希望 catch() rejected 的 promise 而不是 then() 的双情况语法,如下所示。

js
Promise.resolve()
  .then(() => {
    // Makes .then() return a rejected promise
    throw new Error("Oh no!");
  })
  .catch((error) => {
    console.error(`onRejected function called: ${error.message}`);
  })
  .then(() => {
    console.log("I am always called even if the prior then's promise rejects");
  });

在所有其他情况下,返回的 Promise 最终都会 fulfilled。在下面的示例中,第一个 then() 返回一个 fulfilled 的 Promise,其中包含 42,即使链中之前的 Promise 被 rejected 了。

js
Promise.reject()
  .then(
    () => 99,
    () => 42,
  ) // onRejected returns 42 which is wrapped in a fulfilled Promise
  .then((solution) => console.log(`Resolved with ${solution}`)); // Fulfilled with 42

如果 onFulfilled 返回一个 Promise,则 then 的返回值将根据该 Promise 的最终状态而 fulfilled/rejected。

js
function resolveLater(resolve, reject) {
  setTimeout(() => {
    resolve(10);
  }, 1000);
}
function rejectLater(resolve, reject) {
  setTimeout(() => {
    reject(new Error("Error"));
  }, 1000);
}

const p1 = Promise.resolve("foo");
const p2 = p1.then(() => {
  // Return promise here, that will be resolved to 10 after 1 second
  return new Promise(resolveLater);
});
p2.then(
  (v) => {
    console.log("resolved", v); // "resolved", 10
  },
  (e) => {
    // not called
    console.error("rejected", e);
  },
);

const p3 = p1.then(() => {
  // Return promise here, that will be rejected with 'Error' after 1 second
  return new Promise(rejectLater);
});
p3.then(
  (v) => {
    // not called
    console.log("resolved", v);
  },
  (e) => {
    console.error("rejected", e); // "rejected", 'Error'
  },
);

您可以使用链式调用来在一个函数之上,使用基于 Promise 的 API 实现另一个函数。

js
function fetchCurrentData() {
  // The fetch() API returns a Promise. This function
  // exposes a similar API, except the fulfillment
  // value of this function's Promise has had more
  // work done on it.
  return fetch("current-data.json").then((response) => {
    if (response.headers.get("content-type") !== "application/json") {
      throw new TypeError();
    }
    const j = response.json();
    // maybe do something with j

    // fulfillment value given to user of
    // fetchCurrentData().then()
    return j;
  });
}

then() 的异步性

以下是一个示例,用于演示 then 方法的异步性。

js
// Using a resolved promise 'resolvedProm' for example,
// the function call 'resolvedProm.then(...)' returns a new promise immediately,
// but its handler '(value) => {...}' will get called asynchronously as demonstrated by the console.logs.
// the new promise is assigned to 'thenProm',
// and thenProm will be resolved with the value returned by handler
const resolvedProm = Promise.resolve(33);
console.log(resolvedProm);

const thenProm = resolvedProm.then((value) => {
  console.log(
    `this gets called after the end of the main stack. the value received is: ${value}, the value returned is: ${
      value + 1
    }`,
  );
  return value + 1;
});
console.log(thenProm);

// Using setTimeout, we can postpone the execution of a function to the moment the stack is empty
setTimeout(() => {
  console.log(thenProm);
});

// Logs, in order:
// Promise {[[PromiseStatus]]: "resolved", [[PromiseResult]]: 33}
// Promise {[[PromiseStatus]]: "pending", [[PromiseResult]]: undefined}
// "this gets called after the end of the main stack. the value received is: 33, the value returned is: 34"
// Promise {[[PromiseStatus]]: "resolved", [[PromiseResult]]: 34}

规范

规范
ECMAScript 语言规范
# sec-promise.prototype.then

浏览器兼容性

BCD 表格仅在浏览器中加载

另请参阅