首页 > 编程笔记 > JavaScript笔记
JS async和await关键字的用法(非常详细)
JavaScript 中的 async/await 是继 Promise 之后在 ES2017 中新定义的关键字。
async 用于定义异步函数,await 用于获取异步函数的执行结果,它们在语法形式上对 Promise 进行了修改,使代码编写起来更像是同步式的,阅读起来更加直观,但是底层还是基于 Promise 实现的。
例如定义一个异步函数,代码如下:
这里的 await 和后边的代码相当于 then() 中的回调函数,类似下例,代码如下:
需要注意的是,await 只能在异步函数中使用,类似于必须先有 Promise 才能有 then(),如果忘记写 async,则程序会抛出异常。
再来看一个例子,之前使用 fetch() 获取博客文章列表的代码,如果使用 async/await 则可以使用下方示例的形式,代码如下:
也就是说第1个 await 会等待 fetch() 的返回结果,在得到结果之后,第2个 await 才会执行,如果后边有更多的 await 关键字,则它们都会等待前一个执行完毕之后才会执行。
再看《JS Promise用法详解》一文中(靠后的一部分内容)顺序执行 Promise 的例子,这里同样使用之前定义的3个 promise,改成使用 await 的形式顺序执行,代码如下:
这种方式就比使用 then() 的方式清晰了很多。
不过,要想使 await 同时开始执行所有的 promise 或 async函数,可以借助 Promise.all() 实现,例如使用代码:
例如使用 fetch() 处理网络和请求错误,可以使用下方示例的形式,代码如下:
如果想分别捕获 fetch() 和 res.json() 的异常,则可以把它们分别放到两个 try...catch() 语句块中。同样地,也可以使用 finally() 执行一些收尾操作。
async 用于定义异步函数,await 用于获取异步函数的执行结果,它们在语法形式上对 Promise 进行了修改,使代码编写起来更像是同步式的,阅读起来更加直观,但是底层还是基于 Promise 实现的。
定义异步函数
异步函数是使用 async 关键字定义的函数,除了函数名前边需要加上 async 之外,在定义上与普通的函数没有什么区别,不过它的返回值会包装成 Promise 对象。例如定义一个异步函数,代码如下:
async function getTitle(){ return "标题" }如果直接调用这个函数并打印返回值,则会输出:
Promise{<fulfilled >: "标题"}
如果想要访问返回值,则需要像 Promise 一样使用 then(),代码如下:getTitle().then(title=>console.log(title));除此之外,在异步函数中可以使用 await 关键字获取其他 Promise 或异步函数的执行结果。
使用 await
JS await 关键字的作用相当于 then(),Promise 完成之后的值会作为 await 关键字的返回值,可以把它保存到变量中再进行后续操作,代码如下:const promise=new Promise(resolve=>setTimeout(resolve,3*1000,"done")); async function logResult(){ const result=await promise; console.log(result); } logResult();代码中定义了一个 promise,在 3s 后打印出"done"字符串,之后在一个 async 函数 logResult() 中,使用了 await 等待 promise 的执行结果,并打印出来,代码的最后直接调用了 logResult() 这个 async 函数,它没有返回值,所以不需要在后边使用 then()。 运行代码并等待 3s 后,控制台就会打印出"done"。
这里的 await 和后边的代码相当于 then() 中的回调函数,类似下例,代码如下:
promise.then(result=>console.log(result));只不过使用 await 这种方式更符合同步代码的风格。
需要注意的是,await 只能在异步函数中使用,类似于必须先有 Promise 才能有 then(),如果忘记写 async,则程序会抛出异常。
再来看一个例子,之前使用 fetch() 获取博客文章列表的代码,如果使用 async/await 则可以使用下方示例的形式,代码如下:
async function getPosts(){ const res=await fetch("/api/posts"); const posts=await res.json(); return posts; } getPosts().then(posts=>console.log(posts));代码最后同样会打印出获取的文章列表数组,不过这里可以看到,之前使用了两个 then() 分别获取 res 对象和 posts 数组,而这里使用 await 则更像是同步的代码,且两个 await 是按顺序执行的。
也就是说第1个 await 会等待 fetch() 的返回结果,在得到结果之后,第2个 await 才会执行,如果后边有更多的 await 关键字,则它们都会等待前一个执行完毕之后才会执行。
再看《JS Promise用法详解》一文中(靠后的一部分内容)顺序执行 Promise 的例子,这里同样使用之前定义的3个 promise,改成使用 await 的形式顺序执行,代码如下:
async function execPromises(){ await promise1; await promise2; const value3=await promise3; return value3; } execPromises().then(value=>console.log(value));这里最后打印出的结果同样也是 promise3 的返回值:1。
这种方式就比使用 then() 的方式清晰了很多。
不过,要想使 await 同时开始执行所有的 promise 或 async函数,可以借助 Promise.all() 实现,例如使用代码:
await Promise.all([promise, asyncFunc1(), asyncFunc2()])
处理异常
使用 async/await 处理异常的方式也相当直观,可以使用 try...catch 语句块包裹 await 语句,任何一条 await 抛出异常,都能够被 try 捕获,并在 catch 语句块中处理。例如使用 fetch() 处理网络和请求错误,可以使用下方示例的形式,代码如下:
async function getPosts(){ try{ const res=await fetch("/api/posts"); if(res.status>=400){ throw res.status; } const posts=await res.json(); return posts; }catch(error){ if(error===404){ return[]; }else{ console.log(error); } } } getPosts().then(posts=>console.log(posts));如果响应状态码是 404,则 getPosts() 会返回空数组,其他状态码则直接使用 console.log() 打印了出来。
如果想分别捕获 fetch() 和 res.json() 的异常,则可以把它们分别放到两个 try...catch() 语句块中。同样地,也可以使用 finally() 执行一些收尾操作。