Promise.prototype.then()

Baseline 已廣泛支援

此特性已相當成熟,可在許多裝置和瀏覽器版本上使用。自 ⁨2015 年 7 月⁩以來,各瀏覽器均已提供此特性。

Promise 例項的 then() 方法最多接受兩個引數:用於 Promise 的 fulfilled(已fulfilled)和 rejected(已rejected)情況的回撥函式。它將這些回撥函式儲存在呼叫它的 Promise 物件中,並立即返回另一個 Promise 物件,允許你 鏈式呼叫 其他 Promise 方法。

試一試

const promise1 = new Promise((resolve, reject) => {
  resolve("Success!");
});

promise1.then((value) => {
  console.log(value);
  // Expected output: "Success!"
});

語法

js
then(onFulfilled)
then(onFulfilled, onRejected)

引數

onFulfilled

當此 Promise 變為 fulfilled 時非同步執行的函式。其返回值將成為 then() 返回的 Promise 的 fulfillment 值。該函式以以下引數被呼叫:

value

Promise fulfilled 時的值。

如果它不是一個函式,它會被內部替換為一個恆等函式(x) => x),該函式僅將 fulfillment 值向前傳遞。

onRejected 可選

當此 Promise 變為 rejected 時非同步執行的函式。其返回值將成為 then() 返回的 Promise 的 fulfillment 值。該函式以以下引數被呼叫:

reason

Promise rejected 時的值。

如果它不是一個函式,它會被內部替換為一個丟擲函式(x) => { throw x; }),該函式會丟擲它接收到的 rejection reason。

返回值

立即返回一個新的 Promise。這個返回的 Promise 在返回時始終處於 pending(等待)狀態,無論當前 Promise 的狀態如何。

onFulfilledonRejected 這兩個處理程式中的一個將被執行,以處理當前 Promise 的 fulfillment 或 rejection。即使當前 Promise 已經 settled(已定型),呼叫也總是非同步發生的。then() 返回的 Promise(在以下列表中稱為 p)的行為取決於處理程式的執行結果,遵循一套特定的規則。如果處理函式

  • 返回一個值:p 將以返回的值作為其值而被 fulfilled。
  • 不返回任何東西:p 將以 undefined 作為其值而被 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 物件將有兩對 settlement 處理程式。附加到同一 Promise 物件的每個處理程式始終按照它們被新增的順序呼叫。此外,每次呼叫 then() 返回的兩個 Promise 會啟動獨立的鏈,並且不會等待彼此的 settlement。

then() 鏈中出現的 Thenable 物件總是會被 resolvedonFulfilled 處理程式永遠不會接收到一個 thenable 物件,並且任何由這兩個處理程式之一返回的 thenable 在傳遞給下一個處理程式之前都會被 resolved。這是因為在構造新的 Promise 時,executor 傳遞的 resolvereject 函式會被儲存,噹噹前 Promise settle 時,相應的函式將被用 fulfillment 值或 rejection reason 呼叫。resolving 邏輯來自 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(new Error("failed")).then(2, 2).then(console.log, console.log); // Error: failed

鏈式呼叫

then 方法返回一個新的 Promise,這允許進行方法鏈式呼叫。

如果傳遞給 then 作為處理程式的函式返回一個 Promise,那麼鏈式呼叫中的後續 then 將會獲得一個等效的 Promise。下面的程式碼片段使用 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 呼叫返回一個最終 rejected 的 Promise。

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 Promises,而不是 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 最終會 fulfill。在下面的示例中,第一個 then() 返回 42,該值被包裝在一個 fulfilled Promise 中,即使鏈中的前一個 Promise 被 rejected。

js
Promise.reject(new Error("Oh no!"))
  .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");
// Return promise here, that will be resolved to 10 after 1 second
const p2 = p1.then(() => new Promise(resolveLater));
p2.then(
  (v) => {
    console.log("resolved", v); // "resolved", 10
  },
  (e) => {
    // not called
    console.error("rejected", e);
  },
);

// Return promise here, that will be rejected with 'Error' after 1 second
const p3 = p1.then(() => 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® 2026 語言規範
# sec-promise.prototype.then

瀏覽器相容性

另見