JavaScript异步编程
javascript的语言的执行环境是“单线程”。即一次只能完成一件任务。
一般情况下,在浏览器端,耗时很长的操作都应该异步执行,避免浏览器失去响应。所谓异步执行,不同于同步执行(程序的执行顺序与任务的排列顺序是一致的、同步的),每一个任务有一个或多个回调函数(callback),前一个任务结束后,不是执行后一个任务,而是执行回调函数,后一个任务则是不等前一个任务结束就执行,所以程序的执行顺序与任务的排列顺序是不一致的、异步的。
javascript的异步处理能够很好的应对大型Web程序的复杂性,交付快速相应的代码。
javascript的异步处理技巧有:回调函数、事件监听、发布订阅、Promise等
1.回调函数
回调函数是异步编程最基本的方法。
回调函数的优点:简单、容易理解。
假设有两个函数fun1() fun()2,且fun2()等待fun1()执行结果
这里将fun2写成fun1的回调函数
function fun1(callback){
setTimeout(function(){
//fun1的代码
callback();
},1000);
}
//执行代码
fun1 ( fun2 )
缺点:当遇到高阶函数(多层嵌套),代码的可读性差
step1(function(res1){
step2(function(res2){
step3(function(res3){
//...
});
});
});
回调函数还有一个问题:我们在回调函数之外无法捕获到回调函数的异常。
为什么异步代码回调函数中的异常无法被最外层的try/catch语句捕获?
异步调用一般分为两个阶段,提交请求和处理结果,这两个阶段之间有事件循环的调用,它们属于两个不同的事件循环,彼此没有关联。
异步调用一般以传入callback的方式来指定异步操作完成后要执行的动作。而异步调用本体和callback属于不同的事件循环。
try/catch语句只能捕获当次事件循环的异常,对callback无能为力。
也就是说,一旦我们在异步调用函数中扔出一个异步I/O请求,异步调用函数立即返回,此时,这个异步调用函数和这个异步I/O请求没有任何关系。
异步调用一般分为两个阶段,提交请求和处理结果,这两个阶段之间有事件循环的调用,它们属于两个不同的事件循环,彼此没有关联。
异步调用一般以传入callback的方式来指定异步操作完成后要执行的动作。而异步调用本体和callback属于不同的事件循环。
try/catch语句只能捕获当次事件循环的异常,对callback无能为力。
也就是说,一旦我们在异步调用函数中扔出一个异步I/O请求,异步调用函数立即返回,此时,这个异步调用函数和这个异步I/O请求没有任何关系。
2.事件监听
采用事件驱动模式。任务的执行不取决于代码的顺序,而取决于某个事件是否发生。
当fun1发生done事件,就执行fun2:
function fun1(){
setTimesout(function(){
//fun1的任务代码
fun1.trigger('done');
},1000)
}
fun1.on('done',fun2);
3.发布订阅
假设存在一个“信号中心”,某个任务执行完成,就向信号中心“发布”(publish)一个信号,其他任务可以向信号中心“订阅”(subscribe)这个信号,从而知道什么时候可以开始执行。这就是发布订阅 模式。
function fun1(){
setTimeout(function (){
// fun1的任务代码
$.publish('done');
},1000);
}
//fun2向信息中心订阅 'done' 信号
$.subscribe('done',fun2);
这里采用了第三方jQuery的一个插件订阅。
4.Promise
Promises对象是CommonJS工作组提出的一种规范,目的是为异步编程提供统一的接口。Promises被逐渐用作一种管理异步操作回调的方法,但出于它们的设计,它们远比那个有用得多。Promise允许我们以同步的方式写代码,同时给予我们代码的异步执行。
Promise对象:就是把每个异步任务返回一个Promise对象,该对象有一个then方法,允许指定回调函数。
fun1()写法如下:
function fun1(){
var dfd = $.Deferred();
setTimeout(function (){
//fun1的代码
dfd.resolve();
},1000);
return dfd.promise
}
如下,fun1的回调函数fun2
fun1().then(fun2);
若是有多个回调函数可以
fun1().then(fun2).then(fun3);
//指定发生错误的回调函数
fun1().then(fun2).fail(fun3)
该方式的优点在于,回调函数变成了链式写法,程序的流程可以看得很清楚。