我们都知道JavaScript的执行环境是"单线程"(single thread)。就是指一次只能完成一件任务,如果有多个任务,就必须排队,等待前面一个任务完成,再执行后面的任务。
优点:实现起来比较简单,执行环境相对单纯
缺点:只要有一个任务耗时很长,后面的任务都必须排队等着,会拖延整个 程序的执行。我们常见的浏览器会发生无响应(假死),往往就是因为某一段Javascript代码长时间运行(比如死循环),导致整个页面卡在这个地方,其他任务无法执行。
所以在浏览器端,耗时很长的操作都应该异步执行,避免浏览器失去响应。
那么异步编程有哪几种方法呢?
这是异步编程最基本的方法。
优点:简单,容易理解和部署,轻量级。
缺点:不利于代码的阅读和维护,耦合度高,而且流程会很混乱,每个任务只能指定一个回调函数,而且,如果再嵌套多几层,代码会变得很难以理解我们称之为“回调地狱”(callback hell)
function f1(){ console.log(1); f(); };
function f2(){ console.log(2); f(); };
function f3(){ console.log(3);};
f1(function(){ f2(f3); });
我们有两个函数f1和f2,f2等待f1的执行结果。
f1();
f2();
function f1(callback){
setTimeout(function () {
callback();
}, 1000);
}
执行代码就变成下面这样:
f1(f2);
采用这种方式,就把同步操作变成了异步操作,f1不会堵塞程序运行,相当于先执行程序的主要逻辑,将耗时的操作推迟执行。
回调分为同步和异步,区别就是需不需要等待服务器端的返回结果,我们可以在客户端发送消息时加入线程执行,就可以体现异步
为了保证多个异步任务必须顺序执行,而形成了很深的嵌套调用结构(回调地狱),解决方法就是Promise对象,只要是多个异步任务希望循序执行就用此方法。
Promises对象是CommonJS工作组提出的一种规范,目的是为异步编程提供统一接口。简单说,它的思想就是,每一个异步任务返回一个Promise对象,该对象有一个then方法,允许指定回调函数。
Promise1
.then(function(){
return Promise2
})
...
.then(function(){
最后一项任务
})
错误处理:
1.在new Promise中,如果出错,则调用err开关:
new Promise(function(open,err){
//异步任务
//如果正常执行完,调用open()
//如果出错,调用err("错误消息")
})
强调: 调用err()后,后续then不执行
2.在顺序调用的结尾,只添加一个.catch()
.catch(function(err){ ...err变量接住"错误消息" })
前后两个异步调用间传参
前一个异步调用,通过open(参数)方式传参
后一个异步调用,通过.then(function(参数){ ... })方式接受
在ES7中简化 .then()依然使用嵌套方式:
.then(function(){
return 异步调用的函数
})
解决: await:
前提: await修饰的函数必须也是return new Promise()的
如何:
(async function(){ //async说明大任务整体是异步的
try{ //代替.catch(function(err){}) 做错误处理
var result=await 异步调用1
//其中: await是等待当前函数执行完的意思
//阻塞程序执行
//如果异步调用中通过open(参数)返回了数据,可用var result=来接住异步任务返回的数据,在后续步骤中继续使用。
var result=await 异步调用2
...
}catch(err){
...
}
})()
等待多个异步任务执行后才执行:
Promise.all([
异步调用1,
异步调用2,
... ...
])
.then(function(){
...
})
.catch(function(err){ ... })
这样写的优点在于,回调函数变成了链式写法,程序的流程可以看得很清楚,而且有一整套的配套方法,可以实现许多强大的功能。
采用事件驱动模式。任务的执行不取决于代码的顺序,而取决于某个事件是否发生。
优点:比较容易理解,可以绑定多个事件,每个事件可以指定多个回调函数,而且可以"去耦合",有利于实现模块化。
缺点:整个程序都要变成事件驱动型,运行流程会变得很不清晰。
$("#clickity").on("click", function (e) { console.log(1);
我们假定,存在一个"信号中心",某个任务执行完成,就向信号中心"发布"(publish)一个信号,其他任务可以向信号中心"订阅"(subscribe)这个信号,从而知道什么时候自己可以开始执行。这就叫做"发布/订阅模式"(publish-subscribe pattern),又称"观察者模式"(observer pattern)。
这个模式有多种实现,下面采用的是Ben Alman的Tiny Pub/Sub,这是jQuery的一个插件。
首先,f2向"信号中心"jQuery订阅"done"信号。
jQuery.subscribe("done", f2);
然后,f1进行如下改写:
function f1(){
setTimeout(function () {
// f1的任务代码
jQuery.publish("done");
}, 1000);
}
jQuery.publish("done")的意思是,f1执行完成后,向"信号中心"jQuery发布"done"信号,从而引发f2的执行。
此外,f2完成执行后,也可以取消订阅(unsubscribe)。
jQuery.unsubscribe("done", f2);
这种方法的性质与"事件监听"类似,但是明显优于后者。因为我们可以通过查看"消息中心",了解存在多少信号、每个信号有多少订阅者,从而监控程序的运行。
今日话题:你为什么要留在北京?
欢迎来稿!
领取专属 10元无门槛券
私享最新 技术干货