js异步编程

前言

以一个煮饭的例子开始,例如有三件事,A是买菜、B是买肉、C是洗米,最终的结果是为了煮一餐饭。为了最后一餐饭,可以三件事一起做,也可以轮流做,也可能C需要最后做(等A、B做完),这三件事是相关的,抽象起来有三种场景。

顺序做

先买菜,再买肉,最后洗米, 即 A->B->C。

并发做

买菜,买肉,洗米,一起做。

交集做

买菜,买肉必须先做完,才能做洗米。

场景就是这样,接下来就是如何考虑用js实现。

function A(callback){
    setTimeout(function(){
          callback("买好菜了");
     }, 1000);
}
function B(callback){
     setTimeout(function(){
          callback("买好肉了");
     }, 2000);
}
function C(callback){
     setTimeout(function(){
          callback("洗米好了");
     }, 3000);
} 

普通做法(无任何模式)

顺序做

A(function(res){
     console.log(res);
     B(function(res){
          console.log(res);
          C(function(res){
               console.log(res);
               console.log("煮饭啦~~~");
          });
     });
});

这就是传说中的回调地狱,实际的场景嵌套的层级可能还更多。

并发做

var cook_count = 0;
A(function(res){
     callback(res);
});
B(function(res){
     callback(res);
});
C(function(res){
     callback(res);
});
function callback(res){
     console.log(res);
     cook_count++;
     if(cook_count == 3){
           console.log("煮饭啦~~~");
     }
}

设置一个计数器,记录事情发展,到计数到指定的值,即为结束。

交集做

交集做也可以使用计数器做。

var cook_count = 0;
A(function(res){
     callback(res);
});
B(function(res){
     callback(res);
});
function callback(res){
     console.log(res);
     cook_count++;
     if(cook_count == 2){
     C(function(res){
         console.log(res);
         console.log("煮饭啦~~~");
     });
     }
}

发布/订阅模式

我在上一篇文章《观察者模式与发布/订阅模式区别》有说明这种模式的特点,以及附上了js实现方式(这种模式有很多种实现的方式,这个只是简单的一种)。我们可以用这种模式来解决js异步编程。

顺序做

function A(){
  setTimeout(function(){
    pubsub.publish("A_FINISH", "买好菜了");
  }, 1000);
}
function B(){
  setTimeout(function(){
    pubsub.publish("B_FINISH", "买好肉了");
  }, 2000);
}
function C(){
  setTimeout(function(){
    pubsub.publish("C_FINISH", "洗米好了");
  }, 3000);
}
A();
pubsub.subscribe("A_FINISH",  B);
pubsub.subscribe("B_FINISH",  C);
pubsub.subscribe("C_FINISH", function(){
  console.log("煮饭啦~~~");
});

PS:还有一直很常用的jquery的自定义事件也是类似的写法(on、trigger)。

并发做

订阅/发布并没有真正优化到异步编程,所以并发做其实还要用其他方式去检查事情完成状态,就如之前的计数。

var cook_count = 0;
pubsub.subscribe("A_FINISH",  callback);
pubsub.subscribe("B_FINISH",  callback);
pubsub.subscribe("C_FINISH",  callback);
function callback(res){
     console.log(res);
     cook_count++;
     if(cook_count == 3){
           console.log("煮饭啦~~~");
     }
}

PS:交集做也是类似,就不举例了。

Promise/Deferred模式

ES6规范包含了Promise,Promise对象可以理解为一次将要执行的操作(常常被用于异步操作),使用了 Promise 对象之后可以用一种链式调用的方式来组织代码,让代码更加直观。。

顺序做

A().then(function(res) {
    console.log(res);
    return B();
}).then(function(res) {
    console.log(res);
    return C();
}).then(function(res) {
    console.log(res);
    console.log("煮饭啦~~~");
});
function A() {
    return new Promise(function (resolve, reject) {
         setTimeout(function(){
            resolve("买好菜了");
        }, 1000);
    });
}
function B() {
    return new Promise(function (resolve, reject) {
          setTimeout(function(){
               resolve("买好肉了");
          }, 2000);
    });
}
function C() {
    return new Promise(function (resolve, reject) {
          setTimeout(function(){
               resolve("洗米好了");
          }, 2000);
    });
}

then方法定义:then(fulfilledAHandler, errorHandler),对应着Promise的resolve, reject。

并发做

Promise.all方法可做并发调用,当所有Promise 对象都变为完成态或失败态时,回调将被执行。

Promise.all([A(), B(), C()]).then(function (result) {
    console.log(result); //["买好菜了", "买好肉了", "洗米好了"]
}); 

第三方框架实现

jQuery之Deferred

Deferred对象(jQuery的回调函数解决方案),对异步编程做了优化,有人总结四种功能。

一、 提供ajax操作的链式写法

$.ajax("test.html")
    .done(function(){ alert("哈哈,成功了!"); })
    .fail(function(){ alert("出错啦!"); });

PS:高于1.5.0版本,返回的是deferred对象,可以进行链式操作。

二、同一操作的多个回调函数

$.ajax("test.html")
     .done(function(){ alert("哈哈,成功了!");} )
     .done(function(){ alert("第二个回调函数!");} );

三、为多个操作指定回调函数

$.when($.ajax("test1.html"), $.ajax("test2.html"))
     .done(function(){ alert("哈哈,成功了!"); })
     .fail(function(){ alert("出错啦!"); });

PS:如果都成功了,就运行done()指定的回调函数;如果有一个失败或都失败了,就执行fail()指定的回调函数。

四、普通操作的回调函数接口

$(function(){
   var A = function(){ //并不是ajax
     var dtd = $.Deferred();
       setTimeout(function(){
          dtd.resolve("买好菜了");
       }, 1000);
      return dtd.promise(); // 返回promise对象
   };

    var B = function(){
      var dtd = $.Deferred();
        setTimeout(function(){
            dtd.resolve("买好肉了");
        }, 2000);
      return dtd.promise(); // 返回promise对象
   };

    var C = function(){
      var dtd = $.Deferred();
        setTimeout(function(){
            dtd.resolve("洗米好了");
        }, 2000);
      return dtd.promise(); // 返回promise对象
   };
   $.when(A(), B(), C())
    .done(function(v1, v2, v3){  console.log("煮饭啦~~~", v1 , v2, v3); })
    .fail(function(){ alert("出错啦!"); });
});

PS:deferred.promise()方法。它的作用是,在原来的deferred对象上返回另一个deferred对象,后者只开放与改变执行状态无关的方法(比如done()方法和fail()方法),屏蔽与改变执行状态有关的方法(比如resolve()方法和reject()方法),从而使得执行状态不能被改变。

总结

发布/订阅模式相对算是一种较为原始的方式,Promise/Deferred模式贡献了一个非常不错的异步任务模型的抽象,而实际上我们可能会常用的是第三方库来做异步编程,毕竟这些比较成熟及方便。另外如果有其他更好的异步编程方式请回复我一下,让我可以进行总结。

参考文献

jQuery的deferred对象详解》 by 阮一峰

 

本文为原创文章,转载请保留原出处,方便溯源,如有错误地方,谢谢指正。

本文地址 :http://www.cnblogs.com/lovesong/p/5323973.html

转载:http://www.cnblogs.com/lovesong/p/5323973.html

时间: 2024-11-02 10:08:45

js异步编程的相关文章

JS魔法堂:深究JS异步编程模型

前言  上周5在公司作了关于JS异步编程模型的技术分享,可能是内容太干的缘故吧,最后从大家的表情看出"这条粉肠到底在说啥?"的结果:(下面是PPT的讲义,具体的PPT和示例代码在https://github.com/fsjohnhuang/ppt/tree/master/apm_of_js上,有兴趣就上去看看吧! 重申主题  <异步编程模型>这个名称确实不太直观,其实今天我想和大家分享的就是上面的代码是如何演进成下面的代码而已. a(function(){ b(functi

剖析Node.js异步编程中的回调与代码设计模式_node.js

NodeJS 最大的卖点--事件机制和异步 IO,对开发者并不是透明的.开发者需要按异步方式编写代码才用得上这个卖点,而这一点也遭到了一些 NodeJS 反对者的抨击.但不管怎样,异步编程确实是 NodeJS 最大的特点,没有掌握异步编程就不能说是真正学会了 NodeJS.本章将介绍与异步编程相关的各种知识. 在代码中,异步编程的直接体现就是回调.异步编程依托于回调来实现,但不能说使用了回调后程序就异步化了.我们首先可以看看以下代码. function heavyCompute(n, callb

Node.js 异步编程之 Callback介绍(一)

 这篇文章主要介绍了Node.js 异步编程之 Callback介绍(一),本文用实例讲解Callback的相关知识,本文是第一篇,下一篇小编会跟进,需要的朋友可以参考下     Node.js 基于 JavaScript 引擎 v8,是单线程的.Node.js 采用了与通常 Web 上的 JavaScript 异步编程的方式来处理会造成阻塞的I/O操作.在 Node.js 中读取文件.访问数据库.网络请求等等都有可能是异步的.对于 Node.js 新人或者从其他语言背景迁移到 Node.js

javascript异步编程和单线程之间的一些疑惑

问题描述 javascript异步编程和单线程之间的一些疑惑 这两天在研究js异步编程机制,查阅了很多资料,中文的英文的都有.大致上对js异步实现有些了解.但有几点不太理解. 第一个问题: 有一点无论是国内的资料还是英文的,都没有讲到到(可能是过于基础,本人是业余前端,对线程这一块不是很了解).我们说javascript是单线程,即一次只能执行一个任务,前一个任务结束,后一个才会执行.这是从抽象后的javascript的语言层面说的,还是指各种编译环境内部本来就是单线程? 比如当Thread P

Node.js 异步编程之 Callback介绍(一)_node.js

Node.js 基于 JavaScript 引擎 v8,是单线程的.Node.js 采用了与通常 Web 上的 JavaScript 异步编程的方式来处理会造成阻塞的I/O操作.在 Node.js 中读取文件.访问数据库.网络请求等等都有可能是异步的.对于 Node.js 新人或者从其他语言背景迁移到 Node.js 上的开发者来说,异步编程是比较痛苦的一部分.本章将由浅入深为大家讲解 Node.js 异步编程的方方面面.从最基础的 callback 到 thunk.Promise.co 直到

理解javascript异步编程_javascript技巧

一.异步机制 JavaScript的执行环境是单线程的,单线程的好处是执行环境简单,不用去考虑诸如资源同步,死锁等多线程阻塞式编程等所需要面对的恼人的问题.但带来的坏处是当一个任务执行时间较长时,后面的任务会等待很长时间.在浏览器端就会出现浏览器假死,鼠标无法响应等情况.所以在浏览器端,耗时很长的操作都应该异步执行,避免浏览器失去响应.所谓异步执行,不同于同步执行(程序的执行顺序与任务的排列顺序是一致的.同步的),每一个任务有一个或多个回调函数(callback),前一个任务结束后,不是执行后一

.NET异步编程总结----四种实现模式代码总结_实用技巧

最近很忙,既要外出找工作又要兼顾老板公司的项目.今天在公司,忙里偷闲,总结一下.NET中的异步调用函数的实现方法,DebugLZQ在写这篇博文之前自己先动手写了本文的所有示例代码,开写之前是做过功课的,用代码说话方有说服力. 本文的内容旨在用最简洁的代码来把异步调用的方法说清楚,园子里的高手老鸟可以绕行,不喜勿喷,非诚勿扰~ lz的前一篇文章简单的说了下异步,主要是从理解上来讲:这篇文章主要写具体的实现方法.实现异步编程有4种方法可供选择,这4种访求实际上也对应着4种异步调用的模式,分为"等待&

详谈javascript异步编程_javascript技巧

异步编程带来的问题在客户端Javascript中并不明显,但随着服务器端Javascript越来越广的被使用,大量的异步IO操作使得该问题变得明显.许多不同的方法都可以解决这个问题,本文讨论了一些方法,但并不深入.大家需要根据自己的情况选择一个适于自己的方法. 本文为大家详细介绍js中的异步编程,具体内容如下 一 关于事件的异步 事件是JavaScript中最重要的一个特征,nodejs就是利用js这一异步而设计出来的.所以这里讲一下事件机制. 在一个js文件中,如果要运行某一个函数,有2中手段

JavaScript 异步编程

异步编程 Async JavaScript 在 Node 面前获得前所未有的重视.本文结合 Trevor Burnham 所著 <Async JavaScript Build More Responsive Apps with Less Code(中文名: JavaScript 异步编程:设计快速响应的网络应用)>一书,梳理 JavaScript 的异步编程的方方面面. 为更好地了解异步开发的来龙去脉,我们先回顾一下 JS 服务端的历史,看到底解决了什么问题(当然是否真的解决是另外一个问题),