关于 JavaScript 中的 async/await,你需要了解的 6 个要点
ECMAScript提案:顶级 await
ECMAScript提案:顶级 await
如果你遇到过类似下面的代码,那么这篇文章将从多方面帮助你😁。
fetchPizzas ()
. then (( pizzas ) = > {
return sortByToppings ( pizzas )
. then (( pizzas ) = > {
return checkDeliveryOptions ( pizzas )
. then (( pizzasWithDelivery ) = > {
return checkBirthdayGift ( pizzasWithDelivery )
. then (( pizza ) = > {
return sendToCustomer ( pizza );
});
});
});
});
Enter fullscreen mode
Exit fullscreen mode
一点背景介绍
很多时候,我们需要按顺序执行一系列任务。例如,文件处理、根据前一次调用结果多次调用数据库,或者按顺序调用多个 API,其中一次调用依赖于另一次调用。
在引入 Promise 之前 async/await,许多人使用回调函数 setTimeOut来模拟他们想要的行为(也就是所谓的回调地狱)。后来,人们开始使用 Promise,这使得代码更易读,但当调用次数很高时,最终还是会陷入同样的困境(也就是所谓的 Promise 地狱)。
异步函数
在 JavaScript 中,函数是 async指通过事件循环异步执行,并使用隐式 Promise 返回结果的程序。此外,其结果的类型必须是 AsyncFunction对象。
这个函数其实就是Promise 和 生成器 的组合 。我不会详细介绍生成器,但它们通常包含一个或多个 yield关键字。
现在让我们看看这个 async函数是如何工作的。假设我们有一个返回字符串的函数:
function hi () {
return ' Hi from JavaScript ' ;
}
hi (); // 'Hi from JavaScript'
Enter fullscreen mode
Exit fullscreen mode
如果在函数前面加上 ` async\promise`,它就不再返回字符串,而是返回一个 Promise,该 Promise 会自动包装字符串值。
async function hi () {
return ' Hi from JavaScript ' ;
}
hi (); // Promise {<resolved>: "Hi from JavaScript"}
Enter fullscreen mode
Exit fullscreen mode
现在,为了从承诺中获得价值,我们像以前一样行事:
hi (). then ( console . log ); // 'Hi from JavaScript'
Enter fullscreen mode
Exit fullscreen mode
你可能想知道这如何能帮助解决 Promise 地狱问题。请耐心听我解释,我们会一步一步地用例子来讲解,这样讲完之后你就会明白了。
等待
该 await关键字使 JavaScript 引擎等待 Promise 被解析/拒绝,并返回其结果。此关键字只能在 async函数内部使用。
const doSomething = async () => {
console . log ( await hi ())
};
// 'Hi from JavaScript'
Enter fullscreen mode
Exit fullscreen mode
你可能会认为,由于 await`resolve` 会强制 JavaScript 引擎等待,因此会消耗一些 CPU 资源。但事实并非如此,因为引擎可以在等待 Promise 被解析/拒绝期间执行其他脚本。此外,这比使用 `resolve` 和 `reject` 要优雅 promises得多 .then。
警告: 如果在普通函数内部尝试调用 async函数 await,将会收到语法错误。
function doSomething () {
await hi (); // Uncaught SyntaxError: await is only valid in async function
}
Enter fullscreen mode
Exit fullscreen mode
一个小陷阱
大多数刚开始使用编程语言的人 async/await都会忘记,他们不能 async在顶层代码中调用函数。这是因为 await普通函数内部不能有 `<script>` 标签,而顶层函数默认就是普通函数。
let response = await hi (); // syntax error in top-level code
console . log ( response );
Enter fullscreen mode
Exit fullscreen mode
不过,你可以将代码包装在一个 async IIFE(立即执行的函数)中,然后直接在函数中调用它:
( async () => {
let response = await hi ();
console . log ( response ); // 'Hi from JavaScript'
...
})();
Enter fullscreen mode
Exit fullscreen mode
更新 :正如 Nick Tyler 在评论中提到的,目前有一个第三阶段的提案,旨在支持 await顶层代码。敬请关注后续进展:
ECMAScript 的顶级 `await` 提案(第三阶段)
ECMAScript提案:顶级 await
冠军:迈尔斯·博林斯
状态:第三阶段
概要
顶层模块 await使模块能够像大型异步函数一样运行:有了顶层模块 await,ECMAScript 模块 (ESM) 可以 await调用资源,导致其他调用它们的模块 import在开始执行其主体之前必须等待。
动机
IIAFE 的局限性
由于该功能 await仅在函数内部可用 async,因此模块可以 await通过将该代码分解为一个函数,在启动时执行的代码中包含该 async功能:
// awaiting.mjs
import { process } from "./some-module.mjs" ;
let output ;
async function main ( ) {
const dynamic = await import ( computedModuleSpecifier ) ;
const data = await fetch ( url ) ;
output = process ( dynamic . default , data ) ;
}
main ( ) ;
export { output } ;
Enter fullscreen mode
Exit fullscreen mode
这种模式也可以立即调用。你可以称之为立即调用的异步函数……
错误处理
正如我之前所说,大多数 async函数都可以用 Promise 编写成普通函数。然而,函数 在错误处理方面 async有所欠缺。如果一个 await 调用失败,异常会被自动捕获,并且 对象会通过隐式返回的 Promise 传递给调用者。 error-proneError
在此之前,我们必须拒绝从普通函数返回的 Promise,并 .catch在调用者中使用 `is`。我见过很多地方,开发人员使用 `try/catch` 语句并抛出一个新的异常,这意味着堆栈跟踪会被重置。
async function hi () {
throw new Error ( " Whoops! " );
};
async function doSomething () {
try {
let response = await hi ();
return response ;
} catch ( err ) {
console . log ( err );
}
}
doSomething ();
Enter fullscreen mode
Exit fullscreen mode
或者,您可以避免这种情况, try/catch因为调用生成的 Promise hi会被拒绝。然后只需使用 ` .catchgetError` 方法来处理错误即可。
async function hi () {
throw new Error ( " Whoops! " );
};
async function doSomething () {
let response = await hi ();
return response ;
}
doSomething (). catch ( err => {
console . log ( err );
});
Enter fullscreen mode
Exit fullscreen mode
如果你认为更适合你的情况,可以完全忽略 catch 语句,直接使用 全局异常处理程序 来处理所有异常。例如,像这样使用 mixin onrejectionhandled属性的代码。 WindowsEventHandlers
window . onrejectionhandled = function ( e ) {
console . log ( e . reason );
}
Enter fullscreen mode
Exit fullscreen mode
承诺所有兼容性
你可以使用 async/await`aside` Promise.all来等待多个 Promise 完成:
const responses = await Promise . all ([
fetch ( ' yashints.dev/rss ' ),
hi (),
...
])
Enter fullscreen mode
Exit fullscreen mode
如果发生错误,它会像往常一样从失败的 Promise 传播, Promise.all然后变成一个异常,您可以使用上述任何方法捕获该异常。
await可以接受“thenable”
类似地 promise.then,如果你有一个具有 .then方法的对象, await它将被接受。这是为了支持这样一种场景:第三方对象不是 Promise,但与 Promise 兼容(它支持 .then),在这种情况下,只需将其与一起使用即可 await。
class Greeting {
constructor ( name ) {
this . name = name ;
}
then ( resolve , reject ) {
console . log ( resolve );
setTimeout (() => resolve ( `Hi ${ this . name } ` ));
}
};
async function greet () {
const greeting = await Greeting ( ' Yaser ' );
console . log ( greeting ); // Hi Yaser
};
greet ();
Enter fullscreen mode
Exit fullscreen mode
async类方法
你可以拥有一个 async类方法。只需在方法名前加上 async`.` 即可。
class Order {
async deliver () {
return await Promise . resolve ( ' Pizza ' );
}
}
new Order ()
. deliver ()
. then ( console . log ); // Pizza
Enter fullscreen mode
Exit fullscreen mode
概括
简单回顾一下我们目前讨论的内容:
async关键字使方法异步化,从而总是返回一个 Promise,并允许 await使用。
await在 Promise 前加上关键字会让 JavaScript 等待该 Promise 被解析/拒绝。如果 Promise 被拒绝,则会抛出异常;否则,会返回结果。
它们共同为我们提供了编写简洁、更易于测试的异步代码的绝佳机会。
有了 async/await它们你就不需要了 .then/.catch,但请注意,它们仍然是基于承诺的。
你可以使用它 Promise.all来等待多个 async函数调用。
async类中 可以包含方法。
我知道网上有很多很棒的文章 async/await,但我还是想涵盖一些我需要经常提醒自己的要点。希望这篇文章能帮助你找到编写简洁异步 JavaScript 所需的大部分内容。
尽情探索这些景点吧。
文章来源:https://dev.to/yashints/6-points-you-need-to-know-about-async-await-in-javascript-1d1o