今天一朋友去大厂面试完之后回来就跟我疯狂吐槽,说问得好深,感觉回答得不太好。
其中有一题就是问:你是如何理解async函数的。
大致面试过程如下:
朋友:async函数可以结合promise用于控制异步流程,以同步的思考方式来编写异步的代码,可以提高代码的可读性。
面试官:你这只是说明了使用的方法还有它的好处,还有其他的吗?它的原理是什么?
朋友:emmm.........
当时心里就在想,大厂不亏是大厂,连语法糖都不放过,不过想想也是,一般大厂如果问到这种简单的问题,回答一般都是要牵涉到原理的,那async/await的原理到底是什么?
原理
async函数的原理其实就是generator函数结合一个自动迭代器。
这里建议先去了解一下ES6中的
我们先来看一个简单的例子:
let it = null;function* gen() { let data = yield getData(); console.log(data);}function getData() { return new Promise((resolve, reject) => { setTimeout(() => { resolve('Hello Generator'); }, 1000); }) .then( (data) => { // 异步数据获取成功后,通过调用迭代器的next方法将data传入data it.next(data); }, ... );}// 创建迭代器it = gen();// 启动generator迭代器,执行到第一个yiled语句it.next();复制代码
这里我们启动it.next(),generator执行到第一个yiled语句后暂停,这时候getData执行,数据异步获取后在内部通过调用it.next(data)将数据data传输到generator内部,这时generator继续执行内部的data就被赋值了'Hello Generator'并打印出来。
// 打印:Hello Generator复制代码
上面我们是通过了Promise内部去控制了Generator的执行,从而让我们能以一种同步的方式去控制异步的流程。这只是一个雏形,我们可不可以以async函数使用方式为蓝本用Generator和Promise去创建这样一种模式呢?答案是可以的,我们只需要加一个会自动迭代Generator的启动器就可以了。
首先我们用Generator模拟一下async函数,先创建两个异步操作p1和p2,用于在1s后异步获取值
let p1 = () => { return new Promise((resolve, reject) => { setTimeout(() => { resolve(1); }, 1000) });}let p2 = () => { return new Promise((resolve, reject) => { setTimeout(() => { resolve(2); }, 1000) });}复制代码
接着我们写一个Generator函数:
function* gen() { let x = yield p1(); console.log(`x: ${x}`); let y = yield p2(); console.log(`y: ${y}`);}复制代码
其实到这里已经非常像async函数了,下面是async版本
async gen() { let x = await p1(); console.log(`x: ${x}`); let y = await p2(); console.log(`y: ${y}`);}复制代码
现在我们就只差一个会自动迭代Generator的启动器就可以完美的模拟async函数了。下面我们来编写一个简单的run函数
function run(gen) { let it = gen(); return Promise.resolve(it.next()) .then( function handleNext(next) { // 结束执行 if (next.done) return; // 传入Promise return Promise.resolve(next.value) .then( (res) => { // 将Promise的结果输入回生成器当中,并继续执行 handleNext(it.next(res)); } ) } )}// 将gen生成器函数传入,gen函数就会执行run(gen);// 打印:// x: 1 // y: 2复制代码
run函数执行会先执行传入的生成器得到一个迭代器,返回的yield对象如果还能继续执行则将会等到当前Promise完成后自动进行下一个迭代。
原理大致就是这样了,继续撸码了。