Js中的异步解决方案

2019-12-25

Js单线程


  1. 同步模式:程序的执行顺序与任务的排列顺序一致。一次只能完成一件任务,如果有多个任务,就必须排队,前面一个任务完成,后面一个任务才能执行。
  2. 异步模式:程序的执行顺序与任务的排列顺序不一致。前一个任务结束后执行回调函数,后一个任务不等前一个任务结束就执行。

js的执行环境是单线程的,也就是同步执行模式,因为只有这样,js才能更好的与浏览器交互。假如js是多线程的,在浏览器中同时操作一个DOM,一个线程要求添加节点,另一个线程要求删除节点,这时浏览器就不知道以哪个线程为准。为了避免此类问题,降低复杂度,js选择只用一个主线程来执行代码,来保证程序执行的一致性。

js单线程的弊端是,当某个任务耗时长时,后面的任务必须排队等着,常常会出现浏览器无响应等情况。因此,耗时长的操作都应该异步执行,避免浏览器失去响应,最好的例子就是Ajax。 js实现异步有如下方案:

1. 回调函数


函数B作为参数传递到函数A里,A执行完异步操作后再执行B,B是A的回调函数。
缺点:高耦合,维护困难,流程混乱,回调地狱,每个任务只能指定一个回调函数。
注意:回调并不一定就是异步,回调也可以是同步

同步回调
1
2
3
4
5
6
7
function getInfo(callback) {
console.log('A');
callback();
}
getInfo(()=>{
console.log('B');
});
异步回调
1
2
3
4
5
6
7
8
9
10
11
12
function getInfo(callback) {
$.ajax(
url: '',
type: 'get',
succes: res => {
callback(res);
}
);
}
getInfo(res=>{
console.log(res);
});



2. 事件监听


采用事件驱动模式,任务的执行取决于某个事件是否发生。可绑定多个事件,每个事件可指定多个回调函数。可看作是发布订阅模式的一个简化版本。
缺点:整个程序都要变成事件驱动型,运行流程不清晰。

1
2
3
4
f1.addEventListener('done',f2); // 绑定事件,'done'事件一旦被触发便执行f2

var event = new Event('done');
f1.dispatchEvent(); // 触发自定义事件(事件广播)



3. 发布/订阅模式


信号中心发布信号,其他事件订阅信号,订阅者收到信号后开始执行。
与事件监听类似,但明显更优。可查看消息中心,了解存在多少信号、每个信号有多少订阅者,从而监控程序运行。

1
2
3
4
5
//这里使用了一款JQuery插件 
jQuery.subscribe("done", f2); // 订阅,'done'事件一旦发布便执行f2
jQuery.unsubscribe("done", f2); // 取消订阅

jQuery.publish("done"); // 发布



4. Promise 异步处理(ES6)


一种异步编程解决方案,相比传统回调函数更加合理和优雅,采用了链式编程,将异步对象和回调函数脱离开来,解决多层回调地狱的问题。

promise对象三种状态:pending等待中, fulfilledrejected
promise构造函数接收两个函数作为参数:resolvereject
只有异步操作的结果,可以决定promise对象的状态。

1
2
3
4
5
6
7
8
var p = new Promise((resolve, reject) {
... resolve(res); // 异步代码执行成功,执行resolve,promise对象状态从pending变为fulfilled
... reject(err); // 异步代码执行失败,执行reject, promise对象状态从pending变为rejected
});
//指定resolve和reject的回调函数
p.then(func1) // resolve
.catch(func2) // reject
.finalluy(func3); // 任何状态都执行



5. Generator 异步处理 (ES6)


封装了多个内部状态状态机,通过调用next()方法,使指针移向下一个状态。
缺点:必须依靠执行器next()来执行。

1
2
3
4
5
6
7
8
9
10
11
function * generator() {
yield p1; // 定义内部不同状态
yield p2;
return 0;
}
// 通过next方法获取每段状态的返回结果
ar hw = generator();
hw.next(); // value:1, done:false
hw.next(); // value:2, done:false
hw.next(); // value:0, done:true
hw.next(); // value:undefine, done:true



6. async: generator语法糖(ES8)


更好的语义,进一步优化promise的链式调用写法,用同步的方式写异步。
内置执行器,再也不用next()了,被认为是异步编程的终极解决方案。
但async并不会取代promise,底层仍然是promise。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function p = new Promise(){...};

// 普通promise写法
p().then(v1=>{...})
.then(v2=>{...});

// 用async改写
async function test() {
const v1 = 1;
const v2 = await p(v1);
const v3 = await p(v2);
return 'ending';
};
test();

// async捕获异常
async function test() {
try {
const data = await p();
} catch (err) {
console.log(err)
}
};