HOME> 2018世界杯德国> 使用 Promise 2026-07-17 16:29:49
使用 Promise

链式调用

连续执行两个或者多个异步操作是一个常见的需求,在上一个操作执行成功之后,开始下一个的操作,并带着上一步操作所返回的结果。在旧的回调风格中,这种操作会导致经典的回调地狱:

jsdoSomething(function (result) {

doSomethingElse(result, function (newResult) {

doThirdThing(newResult, function (finalResult) {

console.log(`得到最终结果:${finalResult}`);

}, failureCallback);

}, failureCallback);

}, failureCallback);

有了 Promise,我们就可以通过一个 Promise 链来解决这个问题。这就是 Promise API 的优势,因为回调函数是附加到返回的 Promise 对象上的,而不是传入一个函数中。

见证奇迹的时刻:then() 函数会返回一个和原来不同的新的 Promise:

jsconst promise = doSomething();

const promise2 = promise.then(successCallback, failureCallback);

第二个 promise(promise2)不仅表示 doSomething() 函数的完成,也代表了你传入的 successCallback 或者 failureCallback 的完成,这两个函数也可以是返回 Promise 对象的异步函数。这样的话,在 promise2 上新增的排在该 promise 后面的回调函数会通过 successCallback 或 failureCallback 返回。

备注:如果你想要一个可以操作的示例,你可以使用下面的模板来创建任何返回 Promise 的函数:

jsfunction doSomething() {

return new Promise((resolve) => {

setTimeout(() => {

// 在完成 Promise 之前的其他操作

console.log("完成了一些事情");

// promise 的兑现值

resolve("https://example.com/");

}, 200);

});

}

该实现会在下面的在旧式回调 API 中创建 Promise部分讨论。

就像这样,你可以创建一个更长的处理链,其中的每个 Promise 都代表了链中的一个异步过程的完成。此外,then 的参数是可选的,catch(failureCallback) 等同于 then(null, failureCallback)——所以如果你的错误处理代码对所有步骤都是一样的,你可以把它附加到链的末尾:

jsdoSomething()

.then(function (result) {

return doSomethingElse(result);

})

.then(function (newResult) {

return doThirdThing(newResult);

})

.then(function (finalResult) {

console.log(`得到最终结果:${finalResult}`);

})

.catch(failureCallback);

你或许会看到这种形式的箭头函数:

jsdoSomething()

.then((result) => doSomethingElse(result))

.then((newResult) => doThirdThing(newResult))

.then((finalResult) => {

console.log(`得到最终结果:${finalResult}`);

})

.catch(failureCallback);

备注:箭头函数表达式可以有隐式返回值;所以,() => x 是 () => { return x; } 的简写。

doSomethingElse 和 doThirdThing 可以返回任何值——如果它们返回的是 Promise,那么会首先等待这个 Promise 的敲定,然后下一个回调函数会接收到它的兑现值,而不是 Promise 本身。在 then 回调中始终返回 Promise 是非常重要的,即使 Promise 总是兑现为 undefined。如果上一个处理器启动了一个 Promise 但并没有返回它,那么就没有办法再追踪它的敲定状态了,这个 Promise 就是“漂浮”的。

jsdoSomething()

.then((url) => {

// fetch(url) 前缺少 `return` 关键字。

fetch(url);

})

.then((result) => {

// result 是 undefined,因为上一个处理器没有返回任何东西。

// 无法得知 fetch() 的返回值,也无法知道它是否成功。

});

通过返回 fetch 调用的结果(一个 Promise),我们既可以追踪它的完成状态,也可以在它完成时接收到它的值。

jsdoSomething()

.then((url) => {

// 添加 `return` 关键字

return fetch(url);

})

.then((result) => {

// result 是一个 Response 对象

});

如果有竞态条件的话,使 Promise 漂浮的情况会更糟——如果上一个处理器的 Promise 没有返回,那么下一个 then 处理器会被提前调用,而它读取的任何值都可能是不完整的。

jsconst listOfIngredients = [];

doSomething()

.then((url) => {

// fetch(url) 前缺少 `return` 关键字。

fetch(url)

.then((res) => res.json())

.then((data) => {

listOfIngredients.push(data);

});

})

.then(() => {

console.log(listOfIngredients);

// listOfIngredients 永远为 [],因为 fetch 请求还没有完成。

});

因此,一个经验法则是,每当你的操作遇到一个 Promise,就返回它,并把它的处理推迟到下一个 then 处理器中。

jsconst listOfIngredients = [];

doSomething()

.then((url) => {

// fetch 调用前面现在包含了 `return` 关键字。

return fetch(url)

.then((res) => res.json())

.then((data) => {

listOfIngredients.push(data);

});

})

.then(() => {

console.log(listOfIngredients);

// listOfIngredients 现在将包含来自 fetch 调用的数据。

});

更加好的解决方法是,你可以将嵌套链扁平化为单链,这样更简单,也更容易处理错误。具体细节将在下面的嵌套部分讨论。

jsdoSomething()

.then((url) => fetch(url))

.then((res) => res.json())

.then((data) => {

listOfIngredients.push(data);

})

.then(() => {

console.log(listOfIngredients);

});

使用 async/await 可以帮助你编写更直观、更类似同步代码的代码。下面是使用 async/await 的相同示例:

jsasync function logIngredients() {

const url = await doSomething();

const res = await fetch(url);

const data = await res.json();

listOfIngredients.push(data);

console.log(listOfIngredients);

}

请注意,除了前面的 await 关键字外,这段代码看起来与同步代码一模一样。唯一的折衷是,可能很容易忘记 await 关键字,这只能在出现类型不匹配(例如试图将承诺作为值使用)时才能解决。

async/await 基于 promise,例如,doSomething() 与之前的函数相同,因此从 promise 到 async/await 所需的重构工作微乎其微。有关 async/await 语法的更多信息,请参阅异步函数和 await 参考。

备注:async/await 的并发语义与普通 Promise 链相同。异步函数中的 await 不会停止整个程序,只会停止依赖其值的部分,因此在 await 挂起时,其他异步任务仍可运行。

荣耀6plus小米4c哪个好
動森南瓜|怎麼種?顏色品種?用途?附萬聖節南瓜家具 DIY 方程式