在我看来,异步的控制主要分为两类:
无依赖的异步执行,各个操作之间没有相互依赖,均为独立,需要所有操作都执行结束后返回值.
例如场景1: 有3份txt数据文件, 要求读取这3份数据文件, 并按读取顺序依次输出文件内容.
若直接回调, 不采用任何异步控制:
|
|
有相互依赖的异步执行,下一个操作需要用到上一个操作的执行结果,相当于需要顺序执行,不能并发.
例如场景2: 让我们来模拟一个真实的mongodb场景, 相对于我这样的初学者而言, 在编写应用时, 数据库CRUD中最易出现回调嵌套, 因为我们往往要利用上一步查询的结果来继续下一步.
现在我们有一份学生考试信息的文档, 记录学生的考试号、姓名、密码、分数等信息, 具体如下
{ examid: 1, username: ‘Tom’, password: md5(‘good’), score: [84, 79, 91, 90] };
{ examid: 1, username: ‘Ross’, password: md5(‘both’), score: [81, 73, 67, 78] };
{ examid: 2, username: ‘Alisa’, password: md5(‘great’), score: [78, 69, 87, 71] };
{ examid: 2, username: ‘Joan’, password: md5(‘root’), score: [98, 76, 89, 90] };
{ examid: 3, username: ‘Mike’, password: md5(‘true’), score: [56, 23, 88, 10] };
需要求出Tom所在考场的学生名单, 我们也是直接回调, 先不采用任何异步控制:
|
|
eventproxy 《深入浅出nodejs》4.3 P79
(1) 多类型异步协作, all方法将handler注册到事件组合上. 当注册时多个事件都触发后, 会调用handler执行, 每个事件传递的数据, 将会依照事件名顺序, 传入handler作为参数.
这里以场景一的实现作为示例, 当然这里不是很恰当, 因为是多类型, 不应该只有读取文件, 应为多种互不相关操作(比如读文件与读数据库)的混合.
|
|
(2) 重复异步协作, after方法适合重复操作(读取多个文件, 调用多次数据库), 将handler注册到N次相同事件, 触发数达到N次后handler将会被调用执行, 每次触发的数据将会按触发顺序, 存为数组作为参数传入.
我们还是以场景一作为示例, 这个就非常相符.
|
|
(3) 持续型异步协作, tail与all类似, 均为注册到事件组合. all()方法的监听器在满足条件后会执行一次, tail()方法的监听器在满足条件执行一次后, 如果组合事件中某事件再被触发, tail()会以新的数据再次执行.
下面的示例就直接拿文档里的示例了, 朴灵大大写的很棒, 膜拜!
|
|
(4) 异常处理, fail方法侦听了error事件,默认处理卸载掉所有handler,并调用回调函数. done方法, 一旦出现error,默认触发error事件(fail)
|
|
(5) 异步事件触发, 使用 emitLater && doneLater,
|
|
async
async主要提供三种方式(以map为例):
完全并行, results汇集了所有callback第二个参数的内容(第一个为err参数).
|
|
让我们再来写一下场景一:
|
|
执行(Series), results同上, 顺序执行可避免回调黑洞, 但并没有异步?是否可以相邻两步间传递结果?
|
|
限制并行(Limit), results同上, limit为每次并发限制的数量, limit = 2 即每次并发两个function.
|
|
存在依赖(Auto), tasks为任务函数列表, 可约定依赖, results同上, concurrency为最大并行task数.
|
|
利用async, 让我们来写一下场景二:
|
|
Promise
核心在于三个状态的转换, Pending、Resolved和Rejected, 只能从Pending -> Resolved(成功), Pending -> Rejected(失败), 不可逆.
用 ES6 的原生promise来实现一下场景二:
|
|
co
基于 ES6 Generator 的异步解决方案, 让异步在编码中书写如同同步, 同时省去Generator执行器的编写
参考 co 函数库的含义和用法
用 co 模块来模拟一下场景二:
|
|
yield关键字接受方法必须thunk化 – thunkify
小结
node的异步控制, 我觉得是node最基础也是最重要的一部分. 避免回调黑洞, 有利于减少嵌套层数, 便于理解和修改, 也能有效利用node事件驱动、非阻塞I/O的特点.
最开始学习时, 就接触了朴灵大大的《深入浅出nodejs》中异步编程章节, 偏底层的具体实现, 现在回过头去看感觉还是有点困难, js功底还不够, 需要努力了.
之后看了alsotang的《Node.js包教不包会》中lesson4、5, 通过lesson中示例对于异步控制终于有了一些感觉.
重要的事情说三遍! 看文档, 看文档, 看文档! 其实之前一直挺排斥看英文文档的, 容易晕, 但是确实文档全面清晰, 而且带有很好的示例, mongodb driver文档就提供了promise、co的例子, 让我有一种豁然开朗的感觉.
异步控制的学习还远远没有结束, 目前只达到了会用的阶段, 之后我会将我的博客异步重写一遍, 再深入研究一下各个异步控制, 比如co中的generator, ES7中的async等等, 前面的路还很长, 加油!