JS读书心得:《JavaScript框架设计》——第12章 异步处理

一、何为异步                              

  执行任务的过程可以被分为发起和执行两个部分。

  同步执行模式:任务发起后必须等待直到任务执行完成并返回结果后,才会执行下一个任务。

  异步执行模式:任务发起后不等待任务执行完成,而是马上执行下一个任务,当任务执行完成时则会收到通知。

  面对IO操作频繁的场景,异步执行模式可在同等的硬件资源条件下提供更大的并发处理能力,也就是更大的吞吐量。

  但由于异步执行模式打破人们固有的思维方式,并且任务的发起和任务的执行是分离的,从而提高编程的复杂度。

  多线程、多进程均可实现异步模式。

    

二、从回调地狱说起                          

  相信大家都听过“回调地狱”这一让人蛋疼由难以避免的异步执行模式副作用。示例:

setTimeout(function(){
  setTimeout(function(){
    setTimeout(function(){
        setTimeout(function(){
        }, 1000)
    }, 1000)
  }, 1000)
}, 1000)

  由于JS是通过异步执行模式来实现多任务并发执行的,因此不可避免地会遇到异步任务连环嵌套的尴尬局面,而回调地狱则是异步任务嵌套的具体表现形式了。

  回调地狱不仅造成代码难以维护,并且会加大调试的难度,一言以蔽之——无法避免的蛋疼:(

 

三、那些舒缓Callback Hell的方案                

  既然回调地狱如此的不优雅但又无法避免,那么有没有一些减轻痛楚的抽象方式来处理回调呢?

  在寻找良药之前,我们需要先了解的是形成回调地狱的原因,从局部看则是在发起异步任务前必须满足某些前置条件,从全局看则是异步执行模式下的流程控制
其实在同步执行模式当中也存在同样的情况,只不过同步执行模式与我们平常思考的方式一致,因此先满足前置条件再执行同步任务则是顺理成章的事情,也没多大
的感觉。但到了异步任务当中则成为突出的问题。想一想,如果异步任务A->异步任务B->异步任务C均以前一个异步任务为前置条件,那么它们
的关系其实也就是同步执行,但代码表达上却被迫要使用异步编码模式,这种内在关系与表现形式的差异就造就出著名的回调地狱了。

  同步执行模式下的流程控制有 if...elseif...else 、 while 和 try...catch..finally 。而我们的终极目标是采用通过的方式来表达异步执行模式下的流程控制。显然在不改变JS语法的情况下这注定是个伪命题。而我们能做的是不断接近而已。

  而@朴灵的EventProxy则是其中一个缓解回调函数之痛的工具库。

  EventProxy作为一个事件系统,通过after、tail等事件订阅方法提供带约束的事件触发机制,“约束”对应“前置条件”,因此我们可以利用这种带约束的事件触发机制来作为异步执行模式下的流程控制表达方式。

  例如,现在需要在任务A和任务B成功执行后才能执行任务C。

/* 同步执行模式 */
try{
  var result4A = execA()
  var result4B = execB()
  var result4C = execC()
}
catch(e){}

/* 异步执行模式 */
// 1. 回调函数方式 —— 出现Callback Hell了!
execA(function(){
  execB(function(){
    execC()
  })
})

// 2. EventProxy
var ep = EventProxy.create('a', 'b', execC)
ep.fail(function $noop$(){})
execA(ep.done('a'))
execB(ep.done('b'))

   可以看到使用EventProxy时回调函数的数目并没有减少,但回调地狱却不见了(验证了回调地狱不是由回调函数引起,而是由异步执行模式下的流程控制引起的)

   但由于EventProxy采用事件机制来做流程控制,而事件机制好处是降低模块的耦合度,但从另一个角度来说会使整个系统结构松散难以看出主干模块,因此通过事件机制实现流程控制必然导致代码结构松散和逻辑离散,不过这可以良好的组织形式来让代码结构更紧密一些。

 

四、认识Promise                        

  这里的Promise指的是已经被ES6纳入囊中的Promises/A+规范及其实现。使用示例:

var p = new Promise(function(resolve, reject){
  resolve("test")
})
p
  .then(function(val){
    console.log(val)
    return val + 1
  }, function(reason){
  })
  .then(function(val){
    console.log(val)
  }, function(reason){
  })

  我是从jQuery.Deferred的promise方法那开始知道有Promise的存在。但Promises/A+到底描述的一个怎样的机制呢?

  1. 表象——API

   
Promises/A+中规定Promise状态为pending(默认值)、fufilled或rejected,其中状态仅能从
pending->fulfilled或pending->rejected,并且可通过then和catch订阅状态变化事件。状态变化事
件的回调函数执行结果会影响Promise链中下一个Promise实例的状态。另外在触发Promise状态变化时是可以携带附加信息的,并且该附加信
息将沿着Promise链被一直传递下去直到被某个Promise的事件回调函数接收为止。而且Promise还提供Promise.all和
Promise.race两个帮助方法来实现与或的逻辑关系,提供Promsie.resolve来将thenable对象转换为Promise对象。

  2. 流程控制

    通过Promise我们可以成功脱离回调地狱。如:



var execAP = Promise.resolve({then:execA})
  , execBP =  Promise.resolve({then:execB})

Promise
  .all(execAP, execBP)
  .then(execC)

    这也是Promise被大家广泛认识的功能。

  3. 信任机制

    由Labjs作者编写的《深入理解Promise五部曲》从另一个角度对Promise进行更深刻的解读。当我们需要通过第三方工具库或接口来控制本地功能模块时,则通过Promise建立一套信任机制,确保本地功能模块在可预测的范围内被第三方操控。

    而Proimse仅作为库开发者的乐高积木,面对普通开发者则需要提供更高层次的抽象。

 

五、认识Generator Function                  

  Generator Function是ES6引入的新特性——生成器函数。通过组合Promise和Generator Function我们就可以实现采用通过的方式来表达异步执行模式下的流程控制了!!!

 

六、相关笔记                            

《JS魔法堂:剖析源码理解Promises/A规范》

《前端翻译:Promises/A+规范》

《JS魔法堂:jsDeferred源码剖析》

《JS魔法堂:jQuery.Deferred(jQuery1.5-2.1)源码剖析》

《JS魔法堂:mmDeferred源码剖析》

《JS魔法堂:ES6新特性——GeneratorFunction介绍》

《JS魔法堂: Native Promise Only源码剖析》

 

七、iPromise                             

  iPromise是我边学异步处理边开发的Promises/A+规范的实现,并且内部已实现了对Generator
Function的支持。经过3次全局重构后现处于v0.8.2,我觉得现在的代码结构阅读起来比较流畅,并且API已固定,预计日后就是打打补丁罢了。
欢迎大家fork来玩玩 iPromise@github

时间: 2024-09-19 09:06:36

JS读书心得:《JavaScript框架设计》——第12章 异步处理的相关文章

javascript框架设计读书笔记之种子模块_javascript技巧

1.命名空间: js里面的命名空间就是使用对象的属性来扩展的.比如,用户定义一个A对象,A对象下面有B属性和C属性,同时B属性和C属性又是对象.因此A={B:{},C:{}},这时用户就可以在B对象和C对象中定义一样的方法,属性了.因此B和C就属于不同的命名空间.我们调用B,C对象里面的方法,就可以通过A.B.like(),A.C.like()调用了.当然A属于window对象中的属性. 但是有一种情况,比如:boke.jsp页面引入了jquery.js以及prototype.js(他们都会在w

javascript框架设计读书笔记之模块加载系统_javascript技巧

模块加载,其实就是把js分成很多个模块,便于开发和维护.因此加载很多js模块的时候,需要动态的加载,以便提高用户体验. 在介绍模块加载库之前,先介绍一个方法. 动态加载js方法: 复制代码 代码如下: function loadJs(url , callback){ var node = document.createElement("script");       node[window.addEventListener ? "onload":"onre

javascript框架设计之框架分类及主要功能

这篇文章主要介绍了javascript框架设计之框架分类及主要功能的相关资料,需要的朋友可以参考下 从内部架构和理念划分,目前JavaScript框架可以划分为5类. 第一种是以命名空间为导向的类库或框架,如果创建一个数组用new Array(),生成一个对象用new Object(),完全的java风格,因此,我们以某一对象为跟,不断为它添加对象和二级对象属性来组织代码,如金字塔般垒起来,早期代表YUI,EXT(so,不是有活力的公司都还用它们) 第二种是以类工厂为导向的框架.著名的有Prot

javascript框架设计之类工厂

 这篇文章主要介绍了javascript框架设计之类工厂的相关资料,非常浅显易懂,有需要的小伙伴可以查看下.     类与继承在javascript的出现,说明javascript已经达到大规模开发的门槛了,在之前是ECMAScript4,就试图引入类,模块等东西,但由于过分引入太多的特性,搞得javascript乌烟瘴气,导致被否决.不过只是把类延时到ES6.到目前为止,javascript还没有正真意义上的类.不过我们可以模拟类,曾近一段时间,类工厂是框架的标配,本章会介绍各种类实现,方便大

javascript框架设计之框架分类及主要功能_javascript技巧

从内部架构和理念划分,目前JavaScript框架可以划分为5类. 第一种是以命名空间为导向的类库或框架,如果创建一个数组用new Array(),生成一个对象用new Object(),完全的java风格,因此,我们以某一对象为跟,不断为它添加对象和二级对象属性来组织代码,如金字塔般垒起来,早期代表YUI,EXT(so,不是有活力的公司都还用它们) 第二种是以类工厂为导向的框架.著名的有Prototype,还有mootools.Base2.Ten,它们基本上除了最基本的命名空间,其它模块都是一

javascript框架设计之类工厂_javascript技巧

类与继承在javascript的出现,说明javascript已经达到大规模开发的门槛了,在之前是ECMAScript4,就试图引入类,模块等东西,但由于过分引入太多的特性,搞得javascript乌烟瘴气,导致被否决.不过只是把类延时到ES6.到目前为止,javascript还没有正真意义上的类.不过我们可以模拟类,曾近一段时间,类工厂是框架的标配,本章会介绍各种类实现,方便大家在自己的框架中或选择时自己喜欢的那一类风格. 1.javascript对类的支持 在其它语言中 ,类的实例都要通过构

《JavaScript框架设计》——第 1 章 种子模块 1.1命名空间

第 1 章 种子模块 种子模块也叫核心模块,是框架的最先执行的部分.即便像jQuery那样的单文件函数库,它的内部也分许多模块,必然有一些模块冲在前面立即执行,有一些模块只有用到才执行,也有一些模块可有可无,存在感比较弱,只在特定浏览器下才运行. 种子模块就是其中的急先锋,它里面的方法不一定要求个个神通广大,设计优良,但一定极具扩展性,常用,稳定.扩展性是指通过它们能将其他模块的方法包进来,让种子像大树一样成长:常用是指绝大多数模块都用到它们,防止做重复工作:稳定是指不能轻易在以后版本就给去掉,

《JavaScript框架设计》——导读

前言 首先说明一下,本书虽是讲解框架设计,但写个框架不是很深奥的事情,重点是建立更为完整的前端知识树.只有自己尝试写个框架,才有机会接触像原型.作用域.事件代理.缓存系统.定时器等深层知识,也才有机会了解applyElement.swapNode.importNode.removeNode.replaceNode.insertAdjacentHTML.createContextualFragment.runtimeStyle等偏门API,也才会知晓像getElementById.getEleme

《JavaScript框架设计》——第 1 章 种子模块1.1 命名空间

第 1 章 种子模块 种子模块也叫核心模块,是框架的最先执行的部分.即便像jQuery那样的单文件函数库,它的内部也分许多模块,必然有一些模块冲在前面立即执行,有一些模块只有用到才执行,也有一些模块可有可无,存在感比较弱,只在特定浏览器下才运行. 种子模块就是其中的急先锋,它里面的方法不一定要求个个神通广大,设计优良,但一定极具扩展性,常用,稳定.扩展性是指通过它们能将其他模块的方法包进来,让种子像大树一样成长:常用是指绝大多数模块都用到它们,防止做重复工作:稳定是指不能轻易在以后版本就给去掉,