异步编程

我们都知道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);

这种方法的性质与"事件监听"类似,但是明显优于后者。因为我们可以通过查看"消息中心",了解存在多少信号、每个信号有多少订阅者,从而监控程序的运行。

今日话题:你为什么要留在北京?

欢迎来稿!

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20181126G1SSK200?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。

扫码关注云+社区

领取腾讯云代金券