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    ,如需转载请自行联系原作者

相关文章
|
2月前
|
存储 安全 JavaScript
云计算浪潮中的网络安全之舵探索Node.js中的异步编程模式
【8月更文挑战第27天】在数字化时代的风帆下,云计算如同一片广阔的海洋,承载着企业与个人的数据梦想。然而,这片海洋并非总是风平浪静。随着网络攻击的波涛汹涌,如何确保航行的安全成为了每一个船员必须面对的挑战。本文将探索云计算环境下的网络安全策略,从云服务的本质出发,深入信息安全的核心,揭示如何在云海中找到安全的灯塔。
|
25天前
|
前端开发 JavaScript 数据库连接
掌握 JavaScript 异步编程:从回调到 Async/Await
在现代 JavaScript 开发中,异步编程是处理非阻塞操作的关键技术。本文从早期的回调函数讲起,逐步过渡到 Promise 和 ES2017 的 async/await 语法,展示了异步编程如何变得更加简洁和强大。通过实用的技巧和最佳实践,帮助开发者避免常见陷阱,提升代码效率和可靠性。
|
22天前
|
XML JSON 前端开发
JavaScript 异步编程
JavaScript 异步编程
22 3
JavaScript 异步编程
|
1天前
|
JavaScript 前端开发 API
探索Node.js中的异步编程模式
【10月更文挑战第4天】在JavaScript的世界中,异步编程是提升应用性能和用户体验的关键。本文将深入探讨Node.js中异步编程的几种模式,包括回调函数、Promises、async/await,并分享如何有效利用这些模式来构建高性能的后端服务。
|
4天前
|
JavaScript 前端开发 调度
探索Node.js中的异步编程模式
在Node.js的世界里,异步编程是核心。本文将带你深入了解异步编程的精髓,通过代码示例和实际案例分析,我们将一起掌握事件循环、回调函数、Promises以及async/await等关键概念。准备好迎接挑战,让你的Node.js应用飞起来!
|
11天前
|
JavaScript 前端开发 开发者
探索Node.js中的异步编程模式
【9月更文挑战第33天】在JavaScript的后端领域,Node.js凭借其非阻塞I/O和事件驱动的特性,成为高性能应用的首选平台。本文将深入浅出地探讨Node.js中异步编程的核心概念、Promise对象、Async/Await语法以及它们如何优化后端开发的效率和性能。
17 7
|
14天前
|
JavaScript 调度 数据库
深入浅出:Node.js中的异步编程与事件循环
【9月更文挑战第30天】在Node.js的世界里,理解异步编程和事件循环是掌握其核心的关键。本文将通过浅显易懂的语言和实际代码示例,带你探索Node.js如何处理并发请求,以及它是如何在幕后巧妙地调度任务的。我们将一起了解事件循环的各个阶段,并学会如何编写高效的异步代码,让你的应用程序运行得更加流畅。
42 10
|
21天前
|
JavaScript 前端开发 数据库
探索Node.js中的异步编程模型
【9月更文挑战第23天】在Node.js的世界里,异步编程是核心的魔法,它让这个平台能够处理高并发请求。本文将带你深入理解Node.js的异步编程模型,通过代码示例和直观的解释,我们将一起揭开异步编程的面纱。
31 16
|
13天前
|
JavaScript 前端开发 开发者
深入理解Node.js中的事件循环和异步编程
【9月更文挑战第31天】本文旨在揭示Node.js背后的强大动力——事件循环机制,并探讨其如何支撑起整个异步编程模型。我们将深入浅出地分析事件循环的工作原理,以及它如何影响应用程序的性能和稳定性。通过直观的例子,我们会展示如何在实际应用中利用事件循环来构建高性能、响应迅速的应用。此外,我们还会讨论如何避免常见的陷阱,确保代码既优雅又高效。无论你是Node.js的新手还是经验丰富的开发者,本篇文章都将为你提供宝贵的洞察和实用技巧。
38 6
|
17天前
|
前端开发 JavaScript
JavaScript中的Promise:简化异步编程
JavaScript中的Promise:简化异步编程