详细讨论nodejs的异步编程
目前需求中涉及大量异步操作,实际页面越来越倾向于单页应用。骨干、棱角、淘汰制等框架未来都可以使用,但异步编程是首先要面对的问题。随着节点的兴起,异步编程成为一个非常热门的话题。经过一段时间的学习和实践,总结了异步编程的一些细节。
1.异步编程的分类
解决异步问题方法大致包括:直接回调、pub/sub模式(事件模式)、异步库控件库(如async、when)、promise、Generator等。1.1回调函数
回调函数是解决异步的常用方法,经常被触及和使用,容易理解,也容易在库或函数中实现。这也是异步编程中经常使用的方法。
但是,回调函数的方法存在以下问题:
1.可能形成邪恶的嵌套金字塔,代码不易阅读;
2.它只能对应一个回调函数,这在很多场景下成为一个限制。
1.2发布/订阅模式(活动)
这种模式也称为事件模式,是回调函数的事件化,在jQuery和其他类库中非常常见。
事件发布订阅者模式本身没有同步和异步调用的问题,但是在node中,emit调用大多是随着事件周期异步触发的。这种模式通常用于分离业务逻辑。事件发布者无需关注注册的回调函数或回调函数的数量。数据可以通过消息灵活传输。
这种模式的优点是:1。通俗易懂;2.它不再局限于回调函数。
不好的是:1。需要使用类库;2.事件和回调函数的顺序非常重要
复制代码如下: variamg=document . query select(# ID);Img。addeventlistener ('load ',function(){//图片加载完成.});Img。addeventlistener ('error ',function(){//有问题.});
上面的代码有两个问题:
A.实际上已经加载了img,然后绑定了load回调函数。因此,回调不会被执行,但相应的回调函数仍然需要执行。
复制代码如下: variamg=document . query select(# ID);函数加载(){ 0.} if(img.complete){ load();} else { img . addeventlistener(' load ',load);} img。addeventlistener ('error ',function(){//有问题.});
B.不能很好地处理异常
结论:事件机制最适合处理同一对象上的重复事件,在绑定回调函数之前不考虑事件。
1.3异步控制库
目前异步库主要有q、when.js、win.js、RSVP.js等。
这些库的特点是代码是线性的,可以从上到下写,符合自然习惯。
缺点是风格不同,不方便阅读,增加学习成本。
1.4承诺
Promise被翻译成中文作为承诺。个人理解是异步完成后,会给出一个外在的结果(成功或失败),并承诺结果不会再改变。换句话说,promise反映了一个操作的最终返回值(Promise代表一个操作单次完成返回的晚间值)。目前ES6规范中引入了Promise,Chrome、firefox等高级浏览器内部都实现了这种原生方法,使用起来相当方便。
下面从以下几个方面分析《无极》的特点:
1.4.1状态
有三种状态:待定、完成和拒绝,其中只能发生两次转换(从待定-完成、待定-拒绝),状态的转换只能发生一次。
1.4.2然后方法
然后方法用于在异步事件完成后指定回调函数。
这个方法可以说是无极的灵魂方法,让无极充满了魔力。有以下具体表现:
a)然后该方法返回Promise。这样就实现了多个异步操作的串行操作。
上图中黄色圆圈1对价值的处理在Promise中是一个复杂的地方。价值的加工分为两种情况:承诺对象和非承诺对象。
当值不是“承诺”类型时,直接使用值作为解析第二个承诺的参数值;当它是Promise类型时,promise2的状态和参数完全由值决定。可以认为promise2是价值的傀儡,promise 2只是连接不同异步的桥梁。
复制代码如下: promise . prototype . then=function(on completed,on rejected){返回新的promise (function (resolve,Reject) {//这里的Promise标记为promethed 2 handle({ on completed : on completed,on rejected 3360 on rejected,resolve : resolve,Reject : Reject })};}函数句柄(延期){ var handleFnif(state===' completed '){ handleFn=delivered . on completed;} else if(state==' rejected '){ handleFn=delivered . onrejected;} var ret=handleFn(值);delivered . resolve(ret);//请注意,此时的解析是promise2}函数resolve (val) {if (val类型的val)的解析。然后==' function') {val。然后(决心);//如果val是承诺对象或类承诺对象,那么promise2的状态完全由val决定;} if(callback) {//callback是指定的回调函数回调(val);}}
b)实现不同异步库之间的转换。
在异步中,有一个名为thenable的对象,这意味着一个带有then方法的对象。只要对象对象具有then方法,它就可以被转换,例如:
复制代码如下: var delivered=$(' aa . Ajax ');//!延期。然后===truevar P=Promise.resolve(延期);p .然后(.)
1.4.3通用承诺/规范
目前关于Promise有Promise/a和Promise/A的规范,说明Promise的实现相当复杂。
复制代码如下:然后(完成处理程序、拒绝处理程序、进度处理程序)
1.4.4注意事项
Promise中的回调函数共享值。在结果处理中,值作为参数传递给相应的回调函数。如果值是一个对象,注意不要轻易修改值。
复制代码如下: var P=promise . resolve({ x : 1 });p . then(function(val){ console . log(' first callback : ' val . x);});p . then(function(val){ console . log(' second callback : ' val . x)})//first callback : 1//second callback : 2
1.5发电机
以上方法都是基于回调函数完成异步操作,无非是封装回调函数。ES6中提出了生成器,增加了解决异步操作的方式,而不是依赖回调函数。
Generator最大的特点就是可以暂停和重启功能,非常有利于解决异步操作。将生成器的暂停与promise的异常处理相结合,可以优雅地解决异步编程问题。具体实施参考:凯尔辛普森
2.异步编程中的问题
2.1异常处理
a)异步事件包括发送异步请求和处理结果两个环节,通过事件循环连接。然后,当试图捕获try catch异常时,需要将它们分开。
复制代码如下:try {asyncEvent(回调);}捕获(错误).}
上面的代码无法在回调中捕获异常,只能在请求发送阶段获取异常。所以有一个问题:如果请求是由两个人发送和处理的,那么异常处理就有问题。
B)promise交付异常,这带来了一些好处,并确保代码在实际项目中不会被阻塞。但是,如果异步事件很多,就不容易找出是哪个异步事件导致了异常。
复制代码代码如下://场景描述:在客户关系管理里面展示价格的报警信息,其中包含竞对的信息。但是获取竞对的信息时间比较长,后端为了避免慢查询,就把一条记录拆成两块分别获取。//第一步:获取价格报警信息,除了竞对信息函数getPriceAlarmData(){ 0返回新的承诺(函数(resolve) { Y.io(url,{ method: 'get ',data: params, function(){ success : function(id,data){ resolve(alarmData)};} } });});}//得到报警信息后,在去获取竞对信息getPriceAlarmData().然后(函数(数据){ //数据渲染,除了竞对信息渲染(数据);返回新的promise(function(resolve){ y . io(URL,{ method: 'get ',data: {alarmList: data}, function(){ success : function(id,compData){ resolve(compData)};} } });});}) //获取完所有数据后进行竞对信息的渲染。然后(函数(数据){ //渲染竞对信息render(data)},function(err) { //异常处理控制台。日志(err);});
可以把上述代码转换成如下:
复制代码代码如下:try{ //获取除竞对以外的报警信息var alarmData=alarmdataexceprate();渲染(alarm data);//根据报警信息查询竞对信息var compare data=getcompare info(报警数据);渲染(比较数据);} catch e(err){控制台。日志(错误。消息);}
在上述例子中把异常处理放到最后进行处理,这样当其中存在某个环节出现异常,我们无法准确知道到底是哪个事件产生的。
2.2 jQuery .延期的的问题
jQuery中也实现了异步操作,但是在实现上不符合承诺/答规范,主要表现在以下几个方面:
a.参数的个数:标准的承诺只能接受一个参数,而jQuery中则可以传递多个参数
复制代码代码如下:函数asyncInJQuery(){ var d=new $ .递延();setTimeout(函数(){ d.resolve(1,2);}, 100);返回d.promise()}asyncInJQuery().然后(函数(val1,val2) { console.log('output: ',val1,val 2);});//output: 1 2
b.结果处理中异常的处理
复制代码代码如下:函数asyncincrypse(){ 0返回新的promise(function(resolve){ setTimeout(function(){ var jsonStr=' { ' name ' : ' mt } ';解析(jsonStr);}, 100);});} asyncInPromise().然后(函数(val){ var d=JSON。解析(val);控制台。日志(d . name);}).然后(null,函数(err){ console。日志('显示错误: '错误。消息);});//显示错误:输入函数asyncInJQuery()意外结束{ var d=new $ .递延();setTimeout(function(){ var jsonStr=' { ' name ' : ' mt } ';d .解决(jsonStr);}, 100);返回d.promise()}asyncInJQuery().然后(函数(val){ var d=JSON。解析(val);控制台。日志(d . name);}).然后(函数(v) { console.log('success: ',v . name);},函数(错误){控制台。日志('显示错误: '错误。消息);});//未捕获语法分析器:意外的输入结束
从中可以看出答应我对回调函数进行了结果处理,可以捕获回调函数执行过程中的异常,而jQuery .延期的却不可以。
版权声明:详细讨论nodejs的异步编程是由宝哥软件园云端程序自动收集整理而来。如果本文侵犯了你的权益,请联系本站底部QQ或者邮箱删除。