本文是我写的一个目录,具体内容会跳转我的其他文章,阅读性可能较差,抱歉
众所周知,JS是单线程运行的,同一时刻只能执行一个任务,如果碰到一个执行时间很长的任务,那么后面所有的任务都需要排队,这样效率过于低下,因此诞生了异步,虽然还是只能执行一个任务,但是可以把耗时长优先级低的任务往后排。
我们按出现的时间顺序来介绍不同的异步方法,便于记忆。
1.回调函数(ES5)
直接上代码:
function foo1(callback){
setTimeout(function(){
callback();
}, 1000)
}
function foo2(){
let i = 10000;
while(i){
console.log(i--);
}
}
foo1(foo2);
console.log('urgent task')
函数 foo2() 需要很久执行完毕,但是在后面有一个紧急任务console.log('urgent task')
需要赶紧执行,那么就使用 foo1() 设置一个定时器 setTimeout() 来回调 foo2() 。原理是利用JS的 event loop 运行机制,利用定时器 setTimeout() 把 foo2() 插入宏任务队列,等下次执行宏任务时才运行。
优点:简单,易部署
缺点:不易于维护和阅读,高度耦合
2.事件监听
这种方式下,异步任务的执行不取决于代码的顺序,而取决于某个事件是否发生。
下面是两个函数 f1 和 f2,编程的意图是 f2 必须等到 f1 执行完成,才能执行。首先,为 f1 绑定一个事件(这里采用的 jQuery 的写法)
f1.on('done', f2);
上面这行代码的意思是,当 f1 发生 done 事件,就执行 f2。然后,对f1进行改写:
function f1() {
setTimeout(function () {
// ...
f1.trigger('done');
}, 1000);
上面代码中,f1.trigger(‘done’)
表示,执行完成后,立即触发 done 事件,从而开始执行f2。
这种方法的优点是比较容易理解,可以绑定多个事件,每个事件可以指定多个回调函数,而且可以"去耦合",有利于实现模块化。缺点是整个程序都要变成事件驱动型,运行流程会变得很不清晰。阅读代码的时候,很难看出主流程。
3.发布订阅
我们假定,存在一个"信号中心",某个任务执行完成,就向信号中心"发布"(publish)一个信号,其他任务可以向信号中心"订阅"(subscribe)这个信号,从而知道什么时候自己可以开始执行。这就叫做"发布/订阅模式"(publish-subscribe pattern),又称"观察者模式"(observer pattern)。
首先,f2 向信号中心 jQuery 订阅 done 信号。
jQuery.subscribe('done', f2);
然后,f1进行如下改写:
function f1() {
setTimeout(function () {
// ...
jQuery.publish('done');
}, 1000);
}
上面代码中,jQuery.publish(‘done’)
的意思是,f1执行完成后,向信号中心 jQuery 发布 done 信号,从而引发f2的执行。
f2 完成执行后,可以取消订阅(unsubscribe)
jQuery.unsubscribe('done', f2);
这种方法的性质与“事件监听”类似,但是明显优于后者。因为可以通过查看“消息中心”,了解存在多少信号、每个信号有多少订阅者,从而监控程序的运行。
4.Promise
见我的文章Promise总结
5.Generator / yield
Generator 是ES6里新出的一个语法
在 function 后加上一个 * 即代表是一个 Generator 构造器,然后在 Generator 构造器中使用 yield 语法配合使用。每次使用 Generator 构造器的 next() 方法就会执行 Generator 的代码,然后每次遇到 yield x
就返回一个对象 {value: x, done: true/false}
,然后“暂停”。返回的 value 就是 yield 后面表达式的返回值,done 表示这个 Generator 是否已经执行结束了。如果 done 为 true,则 value 就是 return 的返回值。
调用 Generator 对象有两个方法:
- 不断地调用 Generator 对象的 next() 方法
function* fib(max) {
var
t,
a = 0,
b = 1,
n = 0;
while (n < max) {
yield a;
[a, b] = [b, a + b];
n ++;
}
return;
}
var f = fib(5);
f.next(); // {value: 0, done: false}
f.next(); // {value: 1, done: false}
f.next(); // {value: 1, done: false}
f.next(); // {value: 2, done: false}
f.next(); // {value: 3, done: false}
f.next(); // {value: undefined, done: true}
- 直接用 for ... of 循环迭代 Generator 对象
'use strict'
//斐波拉契数列
function* fib(max) {
var
t,
a = 0,
b = 1,
n = 0;
while (n < max) {
yield a;
[a, b] = [b, a + b];
n ++;
}
return;
}
for (var x of fib(10)) {
console.log(x); // 依次输出0, 1, 1, 2, 3, ...
}
6.async / await
见我的文章超详细的 async / await
[1].JS 异步编程六种方案
[2].generator - 廖雪峰的官方网站