注册 登录  
 加关注

网易博客网站关停、迁移的公告:

将从2018年11月30日00:00起正式停止网易博客运营
查看详情
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

Mr.7

我将骄傲的活在这个大唐盛世

 
 
 

日志

 
 

async.js以及流程控制模型  

2015-02-17 13:55:46|  分类: 挨踢咋活 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

1.流程控制模型

有关流程控制,如果不结合具体的实现方式讨论,心中可能会有自己心中的模型。但是结合nodejs的流程控制来看,发现完全不是那回事。如果你了解了nodejs的异步流程控制,再回过头去看下,发现只是形式上的变化。

先说说流程控制的理想的通俗易懂的模型:最小的任务体和流程控制逻辑。最小的任务体是最小的操作单元,这个独立完成,不能再细分;每个任务体的组成要素就是:参数和返回值。而流程控制逻辑主要负责在整个流程运转过程中:多个任务体之间是串行运行还是并行运行;多个任务体之间的相互参数依赖。

这种模型中主要涉及到串行并行的实现机制:多进程,多线程都容易理解。如果是多进程,那么参数都是通过string来实现。如果是多线程,参数还可以是内部的object;也就是说可能存在一个任务体的返回值是另一个任务体的入参,而这时返回值的类型可能就是任意的object了。

而js中,特别是异步中,则完全打破了这种约定。nodejs中流行的是异步的回调;也就是说最小的任务体,很可能没有返回值,所有的都是回调函数参数。这个时候它的串行并行又是怎样实现的呢?

其实在我们的所有编程模型中,整个程序的生命周期是:启动,响应事件。这里面其实蕴含着一个消息循环的过程,只不过这个都是在主框架中固定了。类似如此的程序模型是:web,client ui,基于定时调度的后台server,基于消息监控的后台service......基于这种模型,其实大体上来说,都是顺序的模型:初始化的消息其实就是程序的入口;各种定时调度的消息;各种web的请求;各种鼠标和键盘消息;各种自定义的消息.......最后我们其实都是在响应消息而已。

以前的疑惑:就是消息响应时,函数返回了这段消息即结束。现在实际上返回只是告诉你我接收了消息,但是你要知晓处理结果必须另行监听处理完毕消息。这个小小的变化还不至于令人疑惑,但是如果考虑到一个流程中要等待无数的这几步操作,那么困惑不爽就来了。

不看async.js代码,我们大致可以模拟出这种层层嵌套的串行实现模式:回调中有回调。只要有3层回调,写代码的人就受不了。不光是语法的嵌套受不了,还得时刻坚守思维模式的转换到底。

不熟悉js,nodejs,如果我们用纯C++/JAVA来实现这种,可能需要浩荡的工程来执行消息处理后的回调,这里最重要的原因是消息处理后的回调要依赖于消息触发时的上下文,而这里上下文早已相隔千里;我们要实现上下文的在两个消息之间的保存,所耗费精力不少。而js在语法层次上实现了这一点;也就是js的所谓的执行上下文。

再看async.js,它除了实现这种异步的功能,还将回调隐藏的很深,在代码层面上将实现和逻辑呈献结合的很自然、更清爽。

2.async.js的串行和并行

首先说所谓的串行和并行控制

  • each 所谓的并行是:一开始就顺序遍历iterator触发所有,但是回收结果则是等所有执行完毕才触发
  • eachSeries所谓的串行是:触发一个,等这一个执行完毕才触发另一个,直到全部触发
  • 无论串行还是并行,迭代函数其实是可以同步或异步的。
  • 如果是串行的执行但异步的迭代,针对单个流程来说并没有所谓的节省。但是针对CPU而言,在并行那一刻,它可以执行别的功能,做更多的事而不必强求等待
  • 如果是并行的执行但同步的迭代,那么最终其实还是串行的(也就是说each的迭代是同步的,实际上没有所谓的并行的效果出现)

2.1.async.js的迭代

所谓迭代函数:

  • 是有一个回调函数作为参数。
  • 该回调函数是在主函数中已定义
  • 迭代函数实现时,必须要调用该回调函数
  • 该回调函数的主要作用是通知单项的处理结果;
    同时有的该起到串行并行的迭代串联作用

所谓回调:

  • 每个callback一定有被调用的时候,否则就没有存在的意义
  • 对于整个过程,client只用写callback,类似于只写监听,然后注册于主函数
  • 对于整个过程,主函数内部一定会触发callback

最终:

  • 回调 外部定义内部调
  • 迭代的回调 内部定义外部调
  • 主函数调用迭代 迭代会调用迭代的回调 这样主函数就可以通过迭代的回调来接收结果

3.async.js的思考

对于async.js而言,如果从零使用则很简单,只需要严格遵守约定的规则即可:

  • 迭代的参数以及其约定
  • 迭代内部一定要调用迭代的回调
  • 实现结果回收函数作为回调

如果是在现有流程中迁移到async.js中,则可能涉及到部分流程的改造,还是以上的规则:

  • 功能函数的参数规则保持一致
  • 串行并行的流程不用自己实现,尽量遵守

async.js的几个特点:

  • 分清单个功能体以及流程控制功能
  • 实现方法就是 将内部函数作为参数传递给回调函数,让回调函数根据单个功能体的参数实现自定义功能。然后调用回调来实现内部定义的目标。
  • 虽然对于纯洁爱好者而言,那个迭代函数实现时必须要调用回调参数函数这一步难以令人接收,但是对于同步而言,好像没有更好的解决方法
  • 最终还是尽可能的将单个函数功能体实现,然后封装成async.js的格式

如何理解集合和流程控制这两大模块

  • 纯流程控制其实没有迭代之说
  • 但是集合则牵扯到遍历和迭代
  • 流程控制牵扯到函数的返回和另一个函数的入参

控制流程的任务函数有几个约定

  • 最后一个参数是回调函数cb
  • cb要在任务函数内部调用
  • cb的参数是异常,参数...
  • cb的作用也是告知任务体执行完毕,或者开始触发串行调度或者全部执行完毕的通知

流程控制引擎要解决的问题

  • 任务体和流程控制体要分开
  • 任务体的返回和任务体的入参要分开
  • 任务的执行有同步和异步的方式
  • async.js只是通过函数回调在语言层面上来解决这些问题的方法,并不是最好的方法
  • 因为js语言的特点,有了异步的解决特色:消息队列,执行上下文
  • 如果将函数的回调取消,而采用函数的返回对象来实现,则可以彻底解决功能体和流程控制纠缠在一起的问题

4.async.js的compose和waterfall说明

compose和waterfall其实没什么区别,都是顺序调用异步函数;只是调用形式上的差别而已。

对于每个任务函数而言,相同点:

  • 每个任务函数都形如function(param1,param2......callback)
  • 最后的参数都是callback,用来触发下一个任务函数的执行。
    注意callback并不是下一个异步任务函数,
    它负责触发异常或者调用下一个异步函数
    callback(err, param1,param2....)
    如果有异常err,则会直接触发异常
    如果异常err==null,则会触发下一个函数fn(param1,param2......callback)
  • 任务函数可以有零个一个或多个参数

对于启动方式的区别而言,不同点,

  • compose生成的函数传递参数,则为启动参数
  • 而waterfall因为没有参数,必须用第一个任务函数来触发

  评论这张
 
阅读(379)| 评论(0)
推荐 转载

历史上的今天

在LOFTER的更多文章

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2018