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 阮一峰
js异步编程的更多相关文章
- JS魔法堂:深究JS异步编程模型
前言 上周5在公司作了关于JS异步编程模型的技术分享,可能是内容太干的缘故吧,最后从大家的表情看出"这条粉肠到底在说啥?"的结果:(下面是PPT的讲义,具体的PPT和示例代码在h ...
- 深究JS异步编程模型
前言 上周5在公司作了关于JS异步编程模型的技术分享,可能是内容太干的缘故吧,最后从大家的表情看出"这条粉肠到底在说啥?"的结果:(下面是PPT的讲义,具体的PPT和示例代码在h ...
- node.js异步编程的几种模式
Node.js异步编程的几种模式 以读取文件为例: 1.callback function const fs = require('fs'); //callback function fs.readF ...
- 前端分享----JS异步编程+ES6箭头函数
前端分享----JS异步编程+ES6箭头函数 ##概述Javascript语言的执行环境是"单线程"(single thread).所谓"单线程",就是指一次只 ...
- JS异步编程 (2) - Promise、Generator、async/await
JS异步编程 (2) - Promise.Generator.async/await 上篇文章我们讲了下JS异步编程的相关知识,比如什么是异步,为什么要使用异步编程以及在浏览器中JS如何实现异步的.最 ...
- JS异步编程 (1)
JS异步编程 (1) 1.1 什么叫异步 异步(async)是相对于同步(sync)而言的,很好理解. 同步就是一件事一件事的执行.只有前一个任务执行完毕,才能执行后一个任务.而异步比如: setTi ...
- node.js异步编程解决方案之Promise用法
node.js异步编程解决方案之Promise var dbBase = require('../db/db_base'); var school_info_db = require('../db/s ...
- 一个例子读懂 JS 异步编程: Callback / Promise / Generator / Async
JS异步编程实践理解 回顾JS异步编程方法的发展,主要有以下几种方式: Callback Promise Generator Async 需求 显示购物车商品列表的页面,用户可以勾选想要删除商品(单选 ...
- 深入解析js异步编程利器Generator
我们在编写Nodejs程序时,经常会用到回调函数,在一个操作执行完成之后对返回的数据进行处理,我简单的理解它为异步编程. 如果操作很多,那么回调的嵌套就会必不可少,那么如果操作非常多,那么回调的嵌套就 ...
随机推荐
- java中对象的初始化过程
class Parent{ int num = 8;// ->3 Parent(){ //super(); // ->2 //显示初始化 // ->3 //构造代码段 // -> ...
- Cocos2d-x 3.2 学习笔记(八)Action
Action -动作.所有精灵的表现,人机交互的表现,都是动作.cocos2dx 里面封装的动作可谓是丰富! Action有三个子类 1.FiniteTimeAction类是所有在有限时间能够完成的动 ...
- Sybase 数据库新增用户,赋权
Sybase数据库, 需求:新增用户user1,赋予对原数据库中表t_jingyu的查询权限 数据库原用户登陆 isql -U用户 -P密码 -S服务名 sp_addlogin 'user1','us ...
- 优秀教程:使用 CSS3 动画实现的超炫的过渡特效
Codrops 最近分享了一些很酷的图片切换灵感.有三种不同的用例:小的图像幻灯片,大标题幻灯片以及使用透明背景的产品幻灯片.状态转换使用 CSS 动画完成,我们能够定义从任何方向进来的图片的行为. ...
- 学习制作精美 CSS3 按钮效果的10个优秀教程
由于互联网世界正在发生变化,人们往往喜欢那些有更多互动元素的网站,因此现在很多 Web 开发人员也在专注于提高他们的 CSS3 技能,因为 CSS3 技能可以帮助他们在很大的程度上实现所需的吸引力.这 ...
- Web性能测试参数
1.前言 最近在项目中引入了keyless,需要测试一下对web的性能影响.常见的web测试工具有ab.siege.http_load等.我用的是Apache的ab测试工具.在测试web性能之前,首先 ...
- Winform应用程序实现通用遮罩层
在WEB上,我们在需要进行大数据或复杂逻辑处理时,由于耗时较长,一般我们会在处理过程中的页面上显示一个半透明的遮罩层,上面放个图标或提示:正在处理中...等字样,这样用户体验就比较好了,然而如果在Wi ...
- .NET Core中使用Razor模板引擎
一.简介 在MVC以外的场景中,我们往往需要完成一些模板引擎生成代码或页面的工作:在以前我们一般常用的有Razor.NVeocity.VTemplate.虽然所有的模板系统都具有一些共同特征,但 Ra ...
- php的<?php ?>标签匹配
在公司实习了没有多久就开始和同事发现了一个问题,就是之前的人写过的项目经常莫名其妙的出错,仔细的看项目的源代码的时候发现项目中并没有逻辑上的错误,而且各种关系以及数据都没有问题,这是为什么呢.再次认真 ...
- [转]virtualenv建立多个Python独立开发环境
不同的人喜欢用不同的方式建立各自的开发环境,但在几乎所有的编程社区,总有一个(或一个以上)开发环境让人更容易接受. 使用不同的开发环境虽然没有什么错误,但有些环境设置更容易进行便利的测试,并做一些重复 ...