最近在写【重拾前端】系列,下面有几个快速通道,大家自取
【重识前端】原型/原型链和继承
【重识前端】闭包与模块
【重识前端】全面攻破this
【重识前端】一次搞定JavaScript的执行机制
【重识前端】什么是BFC、IFC、GFC 和 FFC
【重识前端】深入内存世界
【重识前端】暴走的异步编程
前言 老规矩,还是先了解一下什么是异步。异步其实是一个相对比较高级的一个概念。
通常来说,程序都是顺序执行,同一时刻只会发生一件事。如果一个函数依赖于另一个函数的结果,它只能等待那个函数结束才能继续执行,从用户的角度来说,整个程序才算运行完毕.
如果说一个事情需要等待上一件事情做完才能做,但是他们之前又没有强耦合关系。这样就在那里干等就毫无意义。特别是现在计算机普遍都有多核CPU的时代。
举一个形象的🌰。
我们现在要去吃海底捞(海底捞打钱!),我们发现要排队,需要排2个小时左右,这个时候我们要干嘛?难道是干等吗?肯定不是!那样蠢蠢的硬等很呆,我们可能会拿出手机刷刷朋友圈,或者玩一把紧张刺激的王者荣耀又或者去看一场电影等等….在我们做这些事情的时候我们的排队并没有被耽误,甚至可以和服务员说快到了打个电话给我(这个是常见的callback方法,待会讨论)
这样你可以同时完成其他工作,这就是异步编程 的出发点。
本文总结了几个异步的方案,理解它们可以让你写出结构更合理、性能更出色、维护更方便的Javascript程序。
异步callback 上来就是一个英俊的例子
1 2 var response = fetch('myImage.png' );var blob = response.blob();
没有人可以完美预测到每种网络情况下这个response,是否可以完美获得fetch获得的内容,很有可能会报错。
这个时候我们的callback就✨bu ling bu ling的登场啦。
有些小伙伴可能觉得callback似乎和自己没有太大关系。。。emmmm….
1 2 3 btn.addEventListener('click' , () => { alert('You clicked me!' ); });
这样呢?有没有感觉有点儿熟悉了,其他这个就相当于react中的
1 2 3 <div onclick ={() => {alert('You clicked me!');}}> 点我 </div >
是不是很熟悉,这个其实就是最常见的callback(回调函数)。
又或者比较常见的
1 2 3 setTimeout(() => { alert('You clicked me!' ); },2000 )
很常见,大致意思就是2秒钟之后会回调一下这个匿名函数,虽然不一定会准确执行,这个涉及到事件循环,感兴趣的可以去找一下我前阵子写的事件循环😊。
发布-订阅模式 发布-订阅模式:又称为观察者模式,它定义对象间的一种一对多的依赖关系,当一个对象的状态改变时,所有依赖于它的对象都将得到通知。
在写React的时候,有一个非常非常非常常见的🌰:
1 2 3 4 5 6 7 8 9 10 import React, {useState, useEffect} from 'react' ;const test = () => { const [derrick, setDerrick] = useState(0 ) useEffect(() => { console .log(derrick) }, [derrick]) return ( <button onClick={() => {setDerrick(derrick+1 )}}>点我</button> ) }
useEffect订阅了derrick这个变量的变化,然后监听到变化之后就会进行一次log,打印出derrick变量的值。
Promise 回调救星 之前介绍过回调函数这种异步编程。
加入我希望在A执行完之后执行B,然后执行C,然后执行D….最后执行Z
写出来的代码可能是这样的
1 2 3 4 5 6 7 8 9 10 11 12 13 var sayhello = function (name, callback ) { setTimeout(function ( ) { console .log(name); callback(); }, 1000 ); } sayhello("first" , function ( ) { sayhello("second" , function ( ) { sayhello("third" , function ( ) { console .log("end" ); }); }); });
像本例一样嵌套多个方法调用会创建错综复杂的代码,会难以理解与调试。当想要实现更复 杂的功能时,回调函数也会存在问题:若想让两个异步操作并行运行,并且在它们都结束后 提醒你,那该怎么做?若想同时启动两个异步操作,但只采用首个结束的结果,那又该怎么 做?
在这些情况下,你需要追踪多个回调函数并做清理操作, 不过好在promise横空出世拯救了世界。
涉及面试题
promise用过吗?请你介绍一下promise。
其实这里很多同学可能就不知道怎么介绍了,可能只停留在怎么用。我们可以从几个方面来介绍。
promise是什么?
promise解决了什么问题
promise的优缺点
promise是什么? 所谓Promise
,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。
promise解决了什么问题?
解决可读性的问题
解决信任问题
可读性 可读性,之前的回调地狱promise就可以优雅的解决。(还有更加优雅的,待会会介绍。)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 var sayhello = function (name ) { return new Promise (function (resolve, reject ) { setTimeout(function ( ) { console .log(name); resolve(); }, 10000 ); }); } sayhello("first" ).then(function ( ) { return sayhello("second" ); }).then(function ( ) { return sayhello("third" ); }).then(function ( ) { console .log('end' ); }).catch(function (err ) { console .log(err); })
信任问题 这个问题可以用promise的几个特点来解释:
(1)对象的状态不受外界影响。Promise
对象代表一个异步操作,有三种状态:pending
(进行中)、fulfilled
(已成功)和rejected
(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是Promise
这个名字的由来,它的英语意思就是“承诺”,表示其他手段无法改变。
(2)一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise
对象的状态改变,只有两种可能:从pending
变为fulfilled
和从pending
变为rejected
。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时就称为 resolved(已定型)。如果改变已经发生了,你再对Promise
对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。
由于Promise只能被决议一次,且决议之后无法改变,所以,即便是多次回调,也不会影响结果,决议之后的调用都会被忽略。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 function method (cb ) { setTimeout(function ( ) { cb && cb(); cb && cb(); },1000 ); } function method2 ( ) { return new Promise (resolve => { setTimeout(function ( ) { resolve(); resolve(); },1000 ); }) }
控制反转。体现在对于函数的把控,比如this等。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 function method (cb ) { setTimeout(function ( ) { cb && cb.call({a :1 ,b :2 }); },1000 ); } function method2 ( ) { return new Promise (resolve => { setTimeout(function ( ) { resolve(); },1000 ); }) }
手撕A+规范的promise
面试题:你知道promise的A+规范吗?(本人在浩鲸科技被问到的,当时答的支支吾吾。。。)
我何止是知道,我甚至可以手写一个符合A+规范的promise!
A+规范快速入口,点我
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 function MyPromise (executor ) { let self = this ; self.status = 'pending' ; self.value = null ; self.reason = null ; function resolve (value ) { if (self.status === 'pending' ) { self.value = value self.status = 'fulfilled' ; } } function reject (reason ) { if (self.status === 'pending' ) { self.reason = reason self.status = 'rejected' ; } } executor(resolve, reject); }
promise里面最难的就是then了,我们必须将这个方法写在他的原型链上面,并且他接受两个参数,一个是成功的回调,一个是失败的回调。
废话不多说,开冲!
支持异步 在此之前,我们需要加两个变量。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 function MyPromise (executor ) { let self = this ; self.status = 'pending' ; self.value = null ; self.reason = null ; self.onResolved = []; self.onRejected = []; function resolve (value ) { if (self.status === 'pending' ) { self.value = value self.status = 'fulfilled' ; } } function reject (reason ) { if (self.status === 'pending' ) { self.reason = reason self.status = 'rejected' ; } } executor(resolve, reject); }
颜值高又细心的同学会发现,我多加了两个变量。
1 2 3 4 self.onResolved = []; self.onRejected = [];
这两个变量呢,也非常好理解,就是存储回调的。那么有一个非常非常重要的问题,我写的时候也困扰了好久,为什么一定要是一个数组呢?我存储为一个函数不行吗?到时候执行就行啦。。。
qs,和我想一样的帅哥靓女可能比较多。这里我统一回复:
假如说。以下的情况出现的话,咋办。
1 2 3 4 5 6 7 8 9 10 11 12 13 var p = new Promise ((resolve, reject )=> { setTimeout(() => { resolve(4 ) }, 1000 ) }) p.then((res )=> { console .log(res, 'res' ) }) p.then((res1 )=> { console .log(res1, 'res1' ) })
这种情况出现,如果我们只是一个函数,那么我们的console.log(res, 'res')
这辈子都无法问世,为什么?因为被后门的console.log(res1, 'res')
给覆盖了呀。对不对,如果说这个resolve
他延迟执行,这个时候我们需要做的是把then
函数给保存下来,等到resolve
被执行的时候,依次执行我们所有创建的then
里面的回调函数。
听懂掌声!
没听懂的接着往下看,我们把剩余工作做完,然后举一个例子。马上就能明白了。
刚刚说到,我们补充两个变量,然后写下我们的then
函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 MyPromise.prototype.then = function (onFulfilled, onRejected ) { let self = this ; if (self.status === 'fulfilled' ) { onFulfilled(self.value); } if (self.status === 'rejected' ) { onRejected(self.reason); } if (self.status === 'pending' ) { self.onResolved.push(() => {onFulfilled(self.value)}) self.onRejected.push(() => {onRejected(self.reason)}) } }
然后我们还需要在resolve
或者reject
的时候执行我们存储起来的这些函数。
完整代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 function MyPromise (executor ) { let self = this ; self.status = 'pending' ; self.value = null ; self.reason = null ; self.onResolved = []; self.onRejected = []; function resolve (value ) { if (self.status === 'pending' ) { self.value = value self.status = 'fulfilled' ; self.onResolved.forEach(fn => fn()) } } function reject (reason ) { if (self.status === 'pending' ) { self.reason = reason self.status = 'rejected' ; self.onRejected.forEach(fn => fn()) } } executor(resolve, reject); } MyPromise.prototype.then = function (onFulfilled, onRejected ) { let self = this ; if (self.status === 'fulfilled' ) { onFulfilled(self.value); } if (self.status === 'rejected' ) { onRejected(self.reason); } if (self.status === 'pending' ) { self.onResolved.push(() => {onFulfilled(self.value)}) self.onRejected.push(() => {onRejected(self.reason)}) } }
OK。我们先试试我们自己写的promise。跑一下demo吧。为了给懒同学便捷的调试,直接CV下面就好啦。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 function MyPromise (executor ) { let self = this ; self.status = 'pending' ; self.value = null ; self.reason = null ; self.onResolved = []; self.onRejected = []; function resolve (value ) { if (self.status === 'pending' ) { self.value = value self.status = 'fulfilled' ; self.onResolved.forEach(fn => fn()) } } function reject (reason ) { if (self.status === 'pending' ) { self.reason = reason self.status = 'rejected' ; self.onRejected.forEach(fn => fn()) } } executor(resolve, reject); } MyPromise.prototype.then = function (onFulfilled, onRejected ) { let self = this ; if (self.status === 'fulfilled' ) { onFulfilled(self.value); } if (self.status === 'rejected' ) { onRejected(self.reason); } if (self.status === 'pending' ) { self.onResolved.push(() => { onFulfilled(self.value) }) self.onRejected.push(() => { onRejected(self.reason) }) } } var p = new MyPromise((resolve, reject )=> { setTimeout(() => { resolve(4 ) }, 1000 ) }) p.then((res )=> { console .log(res, 'res' ) }) p.then((res1 )=> { console .log(res1, 'res1' ) })
嗯~如愿输出了,那回到刚刚的问题。我们把回调的地方不要用数组而用函数呢?不用同学们打字了,直接CV👇的代码就好了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 function MyPromise (executor ) { let self = this ; self.status = 'pending' ; self.value = null ; self.reason = null ; self.onResolved = []; self.onRejected = []; function resolve (value ) { if (self.status === 'pending' ) { self.value = value self.status = 'fulfilled' ; self.onResolved.forEach(fn => fn()) } } function reject (reason ) { if (self.status === 'pending' ) { self.reason = reason self.status = 'rejected' ; self.onRejected.forEach(fn => fn()) } } executor(resolve, reject); } MyPromise.prototype.then = function (onFulfilled, onRejected ) { let self = this ; if (self.status === 'fulfilled' ) { onFulfilled(self.value); } if (self.status === 'rejected' ) { onRejected(self.reason); } if (self.status === 'pending' ) { self.onResolved.push(() => { onFulfilled(self.value) }) self.onRejected.push(() => { onRejected(self.reason) }) } } var p = new MyPromise((resolve, reject )=> { setTimeout(() => { resolve(4 ) }, 1000 ) }) p.then((res )=> { console .log(res, 'res' ) }) p.then((res1 )=> { console .log(res1, 'res1' ) })
是不是只有console.log(res1, 'res1')
啦?因为执行then
的时候,我们的resolve还有没有被调用,如果我们有多个then
的话,我们需要把他们存储起来,然后依次调用。所以我们需要用到数组,当然也可以是别的方法,核心思想就是把他们存起来,在需要调用的时候调用。
是不是很好理解?!!
听懂掌声!
而且A+也要求了。
2.2.6:then
may be called multiple times on the same promise.
(promise 的 then
可以链式调用多次)
2.2.6.1:If/when promise
is fulfilled, all respective onFulfilled
callbacks must execute in the order of their originating calls to then
.
(如果或当 promise 转态是 fulfilled 时,所有的 onFulfilled 回调回以他们注册时的顺序依次执行)
2.2.6.2:If/when promise
is rejected, all respective onRejected
callbacks must execute in the order of their originating calls to then
.
(如果或当 promise 转态是 rejected 时,所有的 onRejected 回调回以他们注册时的顺序依次执行)
我们对promise的理解更进一步了。记得把代码改回去,继续往下走。
刚刚我们的demo还不够鲁棒,而且我们的测试用例都是完美情况下。
接下来要剑走偏锋了。
A+:
2.2.1:Both onFulfilled
and onRejected
are optional arguments:
(onFulfilled 和 onRejected 都是可选参数:)
2.2.1.1 If onFulfilled
is not a function, it must be ignored.
(如果 onFulfilled 不是函数,它会被忽略)
2.2.1.2 If onRejected
is not a function, it must be ignored.
(如果 onRejected 不是函数,它会被忽略)
2.2.7.2: If either onFulfilled
or onRejected
throws an exception e
, promise2
must be rejected with e
as the reason.
(如果 onFulfilled 或 onRejected 里抛出了一个异常,那么 promise2 必须捕获这个错误(接受一个 reason 参数))
所以我们首先要做的就是,把错误给提前捕获,然后丢给reject
emmm….接下我贴代码都是一起贴了,因为我怕有同学会掉队,里面也会有相应的注释帮助会走神的同学跟紧这辆五菱宏光。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 function MyPromise (executor ) { let self = this ; self.status = 'pending' ; self.value = null ; self.reason = null ; self.onResolved = []; self.onRejected = []; function resolve (value ) { if (self.status === 'pending' ) { self.value = value self.status = 'fulfilled' ; self.onResolved.forEach(fn => fn()) } } function reject (reason ) { if (self.status === 'pending' ) { self.reason = reason self.status = 'rejected' ; self.onRejected.forEach(fn => fn()) } } try { executor(resolve, reject); } catch (e) { reject(e); } } MyPromise.prototype.then = function (onFulfilled, onRejected ) { let self = this ; if (self.status === 'fulfilled' ) { typeof onFulfilled === 'function' && onFulfilled(self.value); } if (self.status === 'rejected' ) { typeof onFulfilled === 'function' && onRejected(self.reason); } if (self.status === 'pending' ) { typeof onFulfilled === 'function' && self.onResolved.push(() => { onFulfilled(self.value) }) typeof onFulfilled === 'function' && self.onRejected.push(() => { onRejected(self.reason) }) } }
链式调用then 开始准备进入promise的难点以及核心点:链式调用。
2.2.7:then
must return a promise
(then 方法一定返回一个 promise)
我们需要返回新的promise的话,可以简单实现,快速迭代。
1 2 3 4 5 Promise .prototype.then = function (onFulfilled, onRejected ) { let promise2 = new Promise ((resolve, reject )=> { }) return promise2 }
2.2.7.1:If either onFulfilled
or onRejected
returns a value x
, run the Promise Resolution Procedure [[Resolve]](promise2, x)
.
(如果 onFulfilled 或 onRejected 返回的是一个 x,那么它会以[[Resolve]](promise2, x)` 处理解析)
2.2.7.2:If either onFulfilled
or onRejected
throws an exception e
, promise2
must be rejected with e
as the reason.
(如果 onFulfilled 或 onRejected 里抛出了一个异常,那么 promise2 必须捕获这个错误(接受一个 reason 参数))
2.2.7.3:If onFulfilled
is not a function and promise1
is fulfilled, promise2
must be fulfilled with the same value as promise1
.
(如果 onFulfilled 不是一个函数,并且 promise1 状态是 fulfilled,那么 promise2 一定会接受到与 promse1 一样的值 value)
2.2.7.4:If onRejected
is not a function and promise1
is rejected, promise2
must be rejected with the same reason as promise1
.
(如果 onRejected 不是一个函数,并且 promise1 状态是 rejected,promise2 一定会接受到与 promise1 一样的值 reason)
我们需要将返回内容promise化,需要对onFulfilled和onRejected进行错误处理,这个我们之前就处理过了,如果onFulfilled和onRejected没有传参就继续传递,原生🌰来:
1 2 3 4 5 6 7 8 9 10 11 12 var p = new Promise (function (resolve, reject ) { setTimeout(function ( ) { resolve(3 ) }, 1000 ) }); p.then(1 ,1 ) .then('' ,'' ) .then() .then(function (res ) { console .log(res) })
这里不管onFulfilled和onRejected传什么值,只要不是函数,就继续向下传入,直到有函数进行接收;要做的事情太多了,不当当是判断函数这种简单的判断了,所以我们要抽离一个新的函数来复用统一处理。因此我们进行如下完善:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 function MyPromise (executor ) { let self = this ; self.status = 'pending' ; self.value = null ; self.reason = null ; self.onResolved = []; self.onRejected = []; function resolve (value ) { if (self.status === 'pending' ) { self.value = value self.status = 'fulfilled' ; self.onResolved.forEach(fn => fn()) } } function reject (reason ) { if (self.status === 'pending' ) { self.reason = reason self.status = 'rejected' ; self.onRejected.forEach(fn => fn()) } } try { executor(resolve, reject); } catch (e) { reject(e); } } function resolvePromise (promise2, res, resolve, reject ) { if (promise2 === res) { return reject(new TypeError ('循环引用' )) } if ( res !== null && (typeof res === 'object' ) || typeof res === 'function' ) ) { try { let used = false ; let then = res.then; if (typeof then === 'function' ) { then.call( res, value => { if (used) { return ; } used = true ; resolvePromise(promise2, value, resolve, reject); }, reason => { if (used) { return ; } used = true ; reject(reason); } ) } } catch (e) { if (used) { return ; } used = true ; reject(e); } } else { resolve(res); } } MyPromise.prototype.then = function (onFulfilled, onRejected ) { let self = this ; let promise2 = new MyPromise((resolve, reject ) => { if (self.status === 'fulfilled' ) { let res = onFulfilled(self.value); resolvePromise(promise2, res, resolve, reject); } if (self.status === 'rejected' ) { let res = onRejected(self.reason); resolvePromise(promise2, res, resolve, reject); } if (self.status === 'pending' ) { self.onResolved.push(() => { let res = onFulfilled(self.value); resolvePromise(promise2, res, resolve, reject); }) self.onRejected.push(() => { let res = onRejected(self.reason); resolvePromise(promise2, res, resolve, reject); }) } }) }
以上就是符合 Promise/A+ 规范的实现了,如果你对于这部分代码尚有疑问,欢迎在评论中与我互动。
Generator
面试题:介绍一下generator吧。
很多小伙伴可能和我一样,只是会用,但是你硬要我介绍,那我咋搞?generator的拼写是g e n e r a t o r?
“哔………………………………”
其实我重温了一下阮老师的es6课程,发现可以从以下几个方面来介绍。
形式上
generator和普通的函数不同点在于,对一个*
号,函数内部使用了yield表达式
语法上
Generator函数封装了多个内部状态(通过yield表达式定义内部状态)。执行Generator函数时会返回一个遍历器对象(Iterator对象)。也就是说,Generator是遍历器对象生成函数,函数内部封装了多个状态。通过返回的Iterator对象,可以依次遍历(调用next方法)Generator函数的每个内部状态。
调用上
普通函数在调用之后会立即执行,而Generator函数调用之后不会立即执行,而是会返回遍历器对象(Iterator对象)。通过Iterator对象的next方法来遍历内部yield表达式定义的每一个状态。
其实我觉得,如果能把下面的这个案例弄清楚,generator就算过关了。
1 2 3 4 5 6 7 8 9 function *foo (x ) { let y = 2 * (yield (x + 1 )) let z = yield (y / 3 ) return (x + y + z) } let it = foo(5 )console .log(it.next()) console .log(it.next(12 )) console .log(it.next(13 ))
首先 Generator
函数调用和普通函数不同,它会返回一个迭代器
当执行第一次 next
时,传参会被忽略,并且函数暂停在 yield (x + 1)
处,所以返回 5 + 1 = 6
当执行第二次 next
时,传入的参数等于上一个 yield
的返回值,如果你不传参,yield
永远返回 undefined
。此时 let y = 2 * 12
,所以第二个 yield
等于 2 * 12 / 3 = 8
当执行第三次 next
时,传入的参数会传递给 z
,所以 z = 13, x = 5, y = 24
,相加等于 42
async/await
什么是async/await?
其实就一句话:async 函数就是 Generator 函数的语法糖。
generator:
1 2 3 4 5 6 var gen = function * ( ) { var f1 = yield readFile('/etc/fstab' ); var f2 = yield readFile('/etc/shells' ); console .log(f1.toString()); console .log(f2.toString()); };
Async:
1 2 3 4 5 6 var asyncReadFile = async function ( ) { var f1 = await readFile('/etc/fstab' ); var f2 = await readFile('/etc/shells' ); console .log(f1.toString()); console .log(f2.toString()); };
一比较就会发现,async 函数就是将 Generator 函数的星号(*)替换成 async,将 yield 替换成 await,仅此而已。
async对generator进行了以下三点的改进:
内置执行器。 Generator 函数的执行必须靠执行器,所以才有了 co 函数库,而 async 函数自带执行器。也就是说,async 函数的执行,与普通函数一模一样,只要一行。
更好的语义。 async 和 await,比起星号和 yield,语义更清楚了。async 表示函数里有异步操作,await 表示紧跟在后面的表达式需要等待结果。
更广的适用性。 co 函数库约定,yield 命令后面只能是 Thunk 函数或 Promise 对象,而 async 函数的 await 命令后面,可以跟 Promise 对象和原始类型的值(数值、字符串和布尔值,但这时等同于同步操作)。
一个函数如果加上了async,那么就会默认返回一个promise
1 2 3 4 5 6 7 8 9 10 11 async function fn (args ) { } function fn (args ) { return spawn(function *( ) { }); }
注意 await 命令后面的 Promise 对象,运行结果可能是 rejected,所以最好把 await 命令放在 try…catch 代码块中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 async function myFunction ( ) { try { await somethingThatReturnsAPromise(); } catch (err) { console .log(err); } } async function myFunction ( ) { await somethingThatReturnsAPromise().catch(function (err ) { console .log(err); }); }
await 命令只能用在 async 函数之中,如果用在普通函数,就会报错。
1 2 3 4 5 6 7 8 async function dbFuc (db ) { let docs = [{}, {}, {}]; docs.forEach(function (doc ) { await db.post(doc); }); }
Reference
https://es6.ruanyifeng.com/#docs/generator