async 和 await

204 阅读2分钟

await 表达式

await关键字接收一个期约并将其转换为一个返回值或一个抛出的异常。

给定一个期约p,表达式await p会一直等到p落定。如果p兑现,那么await p的值就是兑现p的值。如果p被拒绝,那么await p表达式就会抛出拒绝p的值。

通常并不会使用await来接收一个保存期约的变量,更多的是把它放在一个会返回期约的函数调用前面。

let response = await fetch("/api/user/info")
let profile = await response.json()

await关键字并不会导致你的程序阻塞或者在指定的期约落定前什么都不做,而await只是掩盖了这个事实。

所以任何使用await的代码本身都是异步的。

async 函数

只能在以async关键字声明的函数内部使用await关键字。

使用async意味着该函数的返回值将是一个期约,即便函数体中不出现期约相关的代码。

async function getInfo(url) {
    let response = await fetch(url)
	let profile = await response.json()
    return porfile.data
}

在等候一组并发执行的async函数,可以像使用期约一样直接使用Promise.all()

let [value1, value2] - await Promise.all([getInfo(url1), getInfo(url2)])

实现细节

为了理解async函数的工作原理,了解一下后台做了什么。

若有这样一个函数:

async function f(x) {
    // 函数体
}

可以把这个函数想象成一个返回期约的包装函数,它包装了你原始函数的函数体:

function f(x) {
    return new Promise(function(resolve, reject) {
        try {
            resolve(function(x) {/* ... */})
        } catch(e) {
            reject(e)
        }
    })
}

总的来说

函数前面的关键字 async 有两个作用:

  1. 让这个函数总是返回一个 promise。
  2. 允许在该函数内使用 await

Promise 前的关键字 await 使 JavaScript 引擎等待该 promise settle,然后:

  1. 如果有 error,就会抛出异常 — 就像那里调用了 throw error 一样。
  2. 否则,就返回结果。

这两个关键字一起提供了一个很好的用来编写异步代码的框架,这种代码易于阅读也易于编写。

有了 async/await 之后,我们就几乎不需要使用 promise.then/catch,但是不要忘了它们是基于 promise 的,因为有些时候(例如在最外层作用域)我们不得不使用这些方法。并且,当我们需要同时等待需要任务时,Promise.all 是很好用的。


来自《JavaScript权威指南》第7版现代JavaScript教程