JavaScript 异步编程的几种方式比较
1. JavaScript 是单线程,也是多线程的
JavaScript 面向程序员编程,使用的是单线程模型。
2. 程序员面向单线程编程,但可以通过执行异步任务避免 CPU 消耗
在程序中处理任务,都会分为同步任务和异步任务。
同步任务,指那些必须要占用 CPU 时间片等待执行结束才能得到返回结果的任务。而异步任务则灵活的多,现实生活中我们处理事情也多采用异步的方式。比如,正在上班突然觉得肚子饿了想要吃东西,那么我们只需要拿出手机点开 App 叫个外卖,然后继续工作。当外卖送达后,我们只需要接收送来的外卖就可以填饱肚子了。此时叫外卖的过程,就相当于执行一个异步任务。执行异步任务时,只需要将执行的信息在某个地方登记并触发相应的资源调用,然后就可以做别的事情了。当任务执行完毕(外卖送达),会通过相关通知机制(送餐员打电话)告诉我们任务执行结束,可以拿到返回值进行下一步操作了。
3. JavaScript 异步编程按照规范演进,依次有以下几种方案
a. 回调函数
fs.readFile('/etc/passwd', 'utf-8', function (err, data) { if (err) throw err; console.log(data); });
b. 事件监听
通过两步操作:将 A方法绑定到某个事件上、执行 B方法触发上一步的事件,从而达到异步 A方法的目的。
c. 发布/订阅
通过“注册中心”监听某个信号然后执行 A方法,在 B方法执行后发出与上一步对应的信号,从而触发 A方法的调用。
d. Promise 对象
Promise 对象,是通过自身的状态来控制异步操作的,并且一旦状态发生变化就不会再进行更改。
Promise 对象通过构造函数:
var promise = new Promise(function (resolve, reject) { // ... if (/* 异步操作成功 */){ resolve(value); } else { /* 异步操作失败 */ reject(new Error()); } });
Promise 对象通过原型方法 then 继续后续操作
var p1 = new Promise(function (resolve, reject) { resolve('成功'); }); p1.then(console.log, console.error);
e. Generator 函数
Generator 函数是协程在 ES6 的实现,最大特点就是可以交出函数的执行权(即暂停执行)。整个 Generator 函数就是一个封装的异步任务,或者说是异步任务的容器。异步操作需要暂停的地方,都用yield
语句注明。Generator 函数的执行方法如下。
function* gen(x) { var y = yield x + 2; return y; } var g = gen(1); g.next() // { value: 3, done: false } g.next() // { value: undefined, done: true }
Thunk 函数
co 模块
f. async 函数
ES2017 标准引入了 async 函数,使得异步操作变得更加方便。async 函数其实就是 Generator 函数的语法糖。
async
函数对 Generator 函数的改进,体现在以下四点:
(1)内置执行器
(2)更好的语义
(3)更广的适用性
(4)返回值是 Promise
async 注意点:
(1)await
命令后面的 Promise
对象结果可能是 rejected,因此需要对此进行捕获,以免错误丢失。
(2)多个 await 的操作,如果互相无逻辑关系,可将多个 await 并发处理:Promise.all
(3)await 只能写在 async 函数中
(4)async 函数可以保留运行堆栈
async 函数实现的原理
function spawn(genF) { return new Promise(function(resolve, reject) { const gen = genF(); function step(nextF) { let next; try { next = nextF(); } catch(e) { return reject(e); } if(next.done) { return resolve(next.value); } Promise.resolve(next.value).then(function(v) { step(function() { return gen.next(v); }); }, function(e) { step(function() { return gen.throw(e); }); }); } step(function() { return gen.next(undefined); }); }); }
总结:
Promise 对象、Generator 函数、async 函数三者异步处理方式比较
(1)Promise 的 API(then
、catch
等等)太多,使得实际逻辑展示不清晰
(2)Generator 函数的执行必须依赖执行器
(3)async 函数实现简介,语句清晰