async
/ await
是 ES2017中引入的,为了使异步操作得更加方便,本质上 async
函数是 Generator
函数的语法糖。
async
函数书写的方式跟我们普通的函数书写方式一样,只不过是前面多了一个 async
关键字,并且函数返回的是一个 Promise
对象,所接收的值就是函数 return
的值。
1 | let fn = async function(name) { |
在 async
函数内部可以使用 await
命令,表示等待一个异步函数的返回。await
后面跟着的是一个 Promise
对象,如果不是的话,系统会调用 Promise.resolve()
方法,将其转为一个 resolve
的 Promise
的对象。
1 | let foo = async function() { |
如果 async
函数当中执行出现错误的话,返回的 Promise
就会被 reject
。
1 | let fn = async function() { |
并且,如果 await
后面的 Promise
的状态是 reject
,那么整个 async
函数就会中断执行,错误会被 async
函数的 catch
捕获到。
1 | let foo = async function() { |
所以我们用来操作异步请求时,有可能会出现请求失败的情况 ,这个时候为了防止函数停止运行,我们需要一个try...catch
结构来处理错误代码。
1 | let bar = async function() { |
上面的写法就保证了我们的异步函数不会因为出错而中断执行。
以上就是 async
函数的简单用法,如果大家想要深入了解的话,推荐大家阅读阮一峰老师的《ES6标准入门》。
你以为我会就这么快就水完了?那是不可能的。说起异步的话,我们都知道 js
在执行的时候只有一个主线程,主线程会不停的读取调用栈。
这个时候我们就要说到 setTimeout(fn,0)
了,在我眼中这段代码的意思是尽快的加入当前的调用栈,只要执行完前面的任务,就会来执行它。我们可以脑补一下这个画面。setTimeout
函数对着 js引擎说:大哥,我这儿任务比较紧急,让我插插队呗。js引擎大哥就不耐烦的说:知道了,知道了,后面排队去,前面的完了就到你了。
所以就出现了下面这段代码:
1 | let fn = async function() { |
猜猜看,会依次输出什么?
起初,我认为会输出 100 ,1 ,2
。但其实最终结果是1,2,100
,这不论是在 node
环境还是 chrome
下都是如此。后来我自信思考了一下,我们 await
后面跟着是一个 resolve
的 Promise
对象,本质上还是同步的代码,所以该 async
函数就如同普通函数一样执行。
我们再改造一下上面的代码:
1 | let fn = async function() { |
现在这个代码会依次输出啥?
踩过了上面的那个坑之后,我仔细想了一下,我们 async
函数内部的 setTimeout
虽说是尽快排队,但是 await
命令会在此暂停住,继续往下执行代码,将下面的 setTimeout
先排上队,然后再将 async
内部的排上队。所以这边的代码输出的是 : 1,100,2,2
。
JavaScript 在发展过程中,共经历了回调函数、Promise
对象、Generator
函数,async
函数来处理异步。我们接下来就来看一下 async
函数如何更优雅的处理异步。
假设我们需要分别读取 a
、b
、c
三个文件,具体代码如下:
1 | const fs = require('fs'); |
上面是一个简化版的代码,省略了错误处理。通过上面代码的对比,我们可以看出来 async
函数比起 Promise
的链式操作,以及 Generator
的手动执行,要方便得太多了,代码上也简洁明了,让我们看起来一目了然。
上面就是我眼中的 async
函数,以及我所理解的异步处理方法。如果大家对我所理解的有任何歧义,欢迎大家来一起探讨。