关于 promise 的一种更优雅的写法 async/await 中,await 只会出现在 async 函数中,我们使用 async/await
时,几乎不需要 .then
,因为 await
为我们处理等待;但是在代码的顶层,当我们在 async
函数的外部时,我们在语法上是不能使用 await
的,所以通常添加 .then/catch
去处理最终结果或者 error。
有一种特殊的语法可用一种更舒适的方式使用 promise,称为 “async/await”。它的易于理解和使用简单让人惊讶。
Async 函数
我们从 async
关键字开始。它可以放在函数前,就像这样:
async function f() { return 1; }
函数前的 “async” 意味着一件简单的事情:函数总是会返回 promise。如果代码中有 return <non-promise>
,那么 JavaScript 就会自动将其封装到一个带有该值的 resolved promise 中。
例如,上述代码中返回一个带有结果 1
的 resolved promise,我们可以进行测试:
f
(
)
.
then
(
alert)
;
// 1
…我们可以显式的返回一个 promise,结果相同:
async function f() { return Promise.resolve(1); } f().then(alert); // 1
因此,async
确保函数返回一个 promise,并在其中封装非 promise。很简单对吧?但不仅仅如此。因为还有 await
关键字,它只在 async
函数中工作,而且非常酷。
Await
// 只在 async 函数中工作
let
value =
await
promise;
await
关键字使 JavaScript 等待,直到 promise 得到解决并返回其结果。
下面是一个 promise 在 1s 之后 resolve 的例子:
async function f() { let promise = new Promise((resolve, reject) => { setTimeout(() => resolve("done!"), 1000) }); let result = await promise; // 等待,直到 promise 执行 resolves (*) alert(result); // “done!” } f();
函数在 (*)
行执行“暂停”,并在 promise 被处理时继续执行,result
变成其结果。上述代码在一秒内显示了 “done!”
我们强调:await
字面上是让 JavaScript 等待 promise 完成,然后继续处理结果。这并不会消耗 CPU 资源,因为引擎可以同时处理其他任务:执行其他脚本,处理事件等。
这是一种比 promise.then
更优雅地获取 promise 结果的语法,它更容易阅读和编写。
不能在常规函数中使用
await
如果我们尝试在非 async 函数中使用
await
,就会产生语法错误:function f() { let promise = Promise.resolve(1); let result = await promise; // 语法错误 }
我们用 Promises 链 章节 showAvatar()
示例开始,并使用 async/await
重写它:
- 我们需要用
await
替换.then
调用。 - 此外,我们应该使用
async
函数来工作。
async function showAvatar() { // 读取我们的 JSON let response = await fetch('/article/promise-chaining/user.json'); let user = await response.json(); // 读取 GitHub 用户信息 let githubResponse = await fetch(`https://api.github.com/users/${user.name}`); let githubUser = await githubResponse.json(); // 显示化身 let img = document.createElement('img'); img.src = githubUser.avatar_url; img.className = "promise-avatar-example"; document.body.append(img); // 等待 3 秒 await new Promise((resolve, reject) => setTimeout(resolve, 3000)); img.remove(); return githubUser; } showAvatar();
非常整洁,而且易于阅读,对吧?比之前好多了。
await
在顶层代码中无效刚开始使用
await
的新手往往会忘记这一点,但我们不能在最顶层的代码中编写await
,因为它会无效:// 在顶层代码中导致语法错误 let response = await fetch('/article/promise-chaining/user.json'); let user = await response.json();所以我们需要将 await 代码封装在一个async 函数中。就像上述例子一样。
async/await
和promise.then/catch
我们使用
async/await
时,几乎不需要.then
,因为await
为我们处理等待。我们也可以使用try..catch
替代.catch
。但这通常(并不总是)更方便。但是在代码的顶层,当我们在
async
函数的外部时,我们在语法上是不能使用await
的,所以通常添加.then/catch
去处理最终结果或者 error。与上述示例的
(*)
行一样。
总结
函数前的 async
关键字有两个作用:
- 总是返回 promise。
- 允许在其中使用
await
。
在 promise 之前的 await
关键字,使 JavaScript 等待 promise 被处理,然后:
- 如果有 error,就会产生异常,就像在那个地方调用了
throw error
一样。 - 否则,就会返回值,我们可以给它分配一个值。
它们一起为编写易于读写的异步代码提供了一个很好的框架。
对于 async/await
,我们很少需要编写 promise.then/catch
,但我们不应该忘记它们是基于 promise 的。因为有时(例如,在最外面的范围)我们不得不使用这些方法。Promise.all
也是一个很好的东西,它能够同时等待很多任务。
参考:https://zh.javascript.info/async-await