生成器这种方式需要编写外部的执行器,而执行器的代码写起来一点也不简单。
当然也可以使用一些插件,比如co模块来简化执行器的编写。
在ES7中,加入了async函数来处理异步。
它实际上只是生成器的一种语法糖而已,简化了外部执行器的代码,同时利用await替代yield,async替代生成器的(*)号。下面还是来看个例子:
async function delay(){ await new Promise((resolve) => {setTimeout(()=>{resolve()},2000)}); console.log("go on); } delay();
这个例子我们之前用生成器也写过,其中把生成器的(*)号被换成了async。
async关键字必须写在function的前面。如果是箭头函数,则写在参数的前面:
const delay = async () => {}
在函数中,第一句用了await。它替代了之前的yield。
后面同样需要跟上一个Promise对象。
接下来的打印语句会在上面的异步操作完成后执行。
外部调用时就和正常的函数调用一样,但它的实现原理和生成器是类似的。
因为有了async关键字,所以它的外部一定会有相应的执行器来执行它,并在异步操作完成后执行回调函数。
只不过这一切都被隐藏起来了,由JS引擎帮助我们完成。我们需要做的就是加上关键字,在函数中使用await来执行异步操作。
这样,可以大大的简化异步操作。同时,能够像同步方法一样去处理它们。
接下来我们再来看看更细节的一些问题。await后面必须是一个Promise对象,这个很好理解。
因为该Promise对象会返回给外部的执行器,并在异步动作完成后执行resolve,这样外部就可以通过回调函数处理它,并将结果传递给生成器。
如图:
那如果await后面跟的不是Promise对象又会发生什么呢?
const delay = async () => { let data = await "hello"; console.log(data); }
这样的代码是允许的,不过await会自动将hello字符串包装一个Promise对象。就像这样:
let data = await new Promise((resolve,reject) => resolve("hello"));
创建了Promise对象后,立即执行resolve,并将字符串hello传递给外部的执行器。
外部执行器的回调函数再将这个hello传递回来,并赋值给data变量。
所以,执行该代码后,马上就会输出字符串hello。虽然代码能够这样写,但是await在这里的意义并不大,所以await还是应该用来处理异步方法,同时该异步方法应该使用Promise对象。
async函数里面除了有await关键字外,感觉和其他函数没什么区别,那它能有返回值吗?答案是肯定的,
const delay = async () => { await new Promise((resolve) => {setTimeout(()=>{resolve()},2000)}); return "finish"; } let result = delay(); console.log(result);
在delay函数中先执行等待2秒的异步操作,然后返回字符串finish。外部调用时我用一个变量接收它的返回值。最后输出的结果是:
// 没有任何等待立即输出 Promise { } // 2秒后程序结束
我们可以看到,没有任何等待立即输出了一个Promise对象。
而整个程序是在2秒钟后才结束的。由此看出,获取async函数的返回结果实际上是return出来的一个Promise对象。
假如return后面跟着的本来就是一个Promise对象,那么它会直接返回。但如果不是,则会像await一样包裹一个Promise对象返回。所以,想要得到返回的具体内容应该这样:
const delay = async () => { await new Promise((resolve) => {setTimeout(()=>{resolve()},2000)}); return "finish"; } let result = delay(); console.log(result); result.then(function(data){ console.log("data: