使用 Promise

您所在的位置:网站首页 文档中的函数该怎样操作 使用 Promise

使用 Promise

2024-05-28 12:38| 来源: 网络整理| 查看: 265

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

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 挂起时,其他异步任务仍可运行。



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3