简述 JS 异步发展不同阶段
下面用顺序读取三个文件为例子
Callback
node
早期API
中基本都是采用回调函数的方式。
如果要顺序读取三个文件,这里就会有一个历史经典的问题:回调地区(callback hell
),这里明显的缺陷就是:
- 代码层级嵌套过深,看着就恶心;
- 里外层代码之间强耦合,非常不宜维护,修改其中一层代码,可能会影响到别的很多层都得改;
因此,Promise
横空出世,它的出现很大一部分原因,就是为了解决控制异步函数顺序执行中使用 callback
所带来的回调地狱的问题。
Promise
Promise
是异步编程的一种解决方案,比传统的解决方案callback(回调函数)
更合理和更强大。
它最早是由社区提出和实现,ES6
将其写进了语言标准,统一了用法,原生提供了Promise
对象。
这里发现,虽然Promise + then
解决了回调地狱的问题,但是,不同的异步操作之间多了很多冗余代码,看着也很难受。
Generator + yield
Generator 函数是 ES6
提供的一种异步编程解决方案,语法行为与传统函数完全不同。
通过generator + yield + promise + runByPromise(自动执行)
也完成了异步操作顺序执行的效果。
虽说后面的自动执行可以封装起来,在用的地方直接用就行,但是强依赖于自动执行。
并且function* fnName
的 Generator 的写法总感觉有点不太自由。
async + await
ES2017
标准引入了 async
函数,使得异步操作变得更加方便。
async
函数是什么?一句话,它就是 Generator 函数的语法糖。
相比于generator + yield + promise + runByPromise(自动执行)
,这里不需要 自动执行 这一步,这里其实也能看出,async await
其实就是对自动执行进行了封装。
这是目前异步操作的终极解决方案。
async, await和 Generator 的关系
async, await
就是 Generator + yield
的语法糖。
async
函数的实现原理,就是将 Generator 函数和 Generator 自动执行,包装在一个函数里。
所有的async
函数都可以写成上面的第二种形式,其中的coAsync
函数就是 自动执行器。
下面给出coAsync
函数的实现,也就是 Generator 的 自动执行器(自动执行到 Generator 终止)。
这里我们也看得出为什么async
函数的返回值是一个Promise
。
关于Promise
用法的疑惑请参考:https://es6.ruanyifeng.com/#docs/promise。