js 异步编程的4种方式
背景
js 语言执行环境——单线程(Single Thread)
单线程
一次只能执行一个任务,超过一个的就只能排队
单线程优势
实现简单
执行环境单纯
单线程劣势
存在耗时很长的任务会拖慢整个程序的运行
单线程带来的问题
浏览器无响应(假死)
浏览器假死原因分析
某段 js 代码(某个任务)长时间运行(比如死循环)
js 对于单线程劣势的解决方案
执行模式分为:同步(Synchronous)和异步(Asynchronus)
同步模式
后一个任务等待前一个任务结束
程序执行顺序同任务排列顺序
异步模式
每个任务有 1 个或多个回调函数(callback)
前一个任务结束,不是继续执行后一个任务,而是执行回调
后一个任务不等前一个任务结束就执行
程序的执行顺序和任务排列顺序不一致
异步模式使用
浏览器端耗时长的任务都应该异步执行
避免浏览器失去效应
异步模式实践
Ajax
异步编程的 4 种方式(实践)
1.回调函数(最基本)
实际场景
两个函数 f1()、f2(),f1()耗时长
解决方案
把 f2()函数写成 f1()函数的回调函数
function f1(callback) {
setTimeout(function () {
// code of f1
callback();
}, 1000);
}
f1(f2);
f1 不会阻塞程序执行
先执行程序主逻辑,耗时操作推迟执行
回调函数存在的问题
不利于代码阅读和维护
各部分间耦合高(Coupling)
流程变乱
每个任务仅支持一个回调函数
2.事件监听(信号)
使用事件驱动模式——任务的执行不取决于代码顺序,而是事件的发生
为 f1 绑定一个事件
f1.on("done", f2);
解释:当 f1 发生 done 事件时执行 f2
function f1() {
setTimeout(function () {
f1.trigger("done");
}, 1000);
}
f1.trigger(‘done’)表示当执行完其上的内容,立即触发 done 事件(即执行 f2)
优势
容易理解
每个事件可指定多个回调函数
可以去耦合(decoupling)
有利于实现模块化
缺陷
整个程序变成事件驱动的
运行流程不清晰
3.发布/订阅模式 或 观察者模式(publish/subscribe pattern or observer pattern)
制造一个信号中心,某任务完成后向信号中心**发布(publish)一个信号,其他任务可向信号中心订阅(subscribe)**这个信号,从而得知自己什么时候执行
该模式的多种实现
- Ben Alman’s gist https://gist.github.com/661855 (recommended)
- Rick Waldron’s jQuery-core style take on the above https://gist.github.com/705311
- Peter Higgins’ plugin (http://github.com/phiggins42/bloody-jquery-plugins/blob/master/pubsub.js).
- Ben Truyman’s gist https://gist.github.com/826794
参考:https://msdn.microsoft.com/en-us/magazine/hh201955.aspx
这里举例第一种,是 jQuery 的一个插件
- f2 向 jQuery(信号中心)订阅 done 信号
jQuery.subscribe("done", f2);
- f1 向 jQuery(信号中心)发布 done 信号
function f1() {
setTimeout(function () {
jQuery.publish("done");
});
}
如果 f2 只要执行一次,可以在执行后取消订阅
jQuery.unsubscribe("done", f2);
发布订阅模式优劣分析
与事件监听类似,但可通过信号中心清晰了解到存在多少信号(发布者发布了多少信号)、每个信号有多少订阅者,从而监控程序的执行
4.Promises 对象
Promises 对象是 CommonJS 工作组提出的一种规范,目的旨在为异步编程提供统一的接口
Promises 实现异步编程的思想
每一个异步任务返回一个 Promise 对象
该对象有一个then方法,允许指定回调函数
实例操作
为 f1 指定回调函数为 f2
f1().then(f2);
jQuery 的实现
function f1() {
var dfd = $.Deferred();
setTimeout(function () {
dfd.resolve();
}, 1000);
return dfd.promise;
}
优点分析
回调函数变成了链式写法,程序流程清晰
有配套的整套方法,可实现强大的功能
应用场景
指定多个回调函数
f1().then(f2).then(f3);
指定发生错误时的回调函数
f1().then(f2).fail(f3);
Promises 使用的优缺点
优点
一个任务已完成,再添加回调函数,该回调函数会立刻执行
不用担心是否错过某个事件或信号
缺点
- 难以理解和编写
参考链接: