什么是异步编程
异步编程可以理解为:一个任务A执行到一半时,交出自己的执行权并处于“暂停”状态,转而去执行其他的任务(任务B),然后再条件准备好时,再回过头来执行任务A
在ES6之前,我们一搬会有以下几种方法来实现异步编程:
- 回掉函数
- 事件监听
- 发布/订阅
- Promise 对象(第三方库)
但是在ES6时代,又多了一种异步编程的选择—— Generator 函数
Generator 函数实现一个基本的异步编程
在上一篇中说过,Generator 函数就像一个状态机,调用后返回的遍历器对象每调用一次 next() 方法,遍历器中的状态指针才会向后移动到下一个状态,并执行下一个状态之前的代码,且不印象后面的代码,这就为异步编程提供了良好的条件,使异步编程的书写变得如同同步编程一般
function* generator() {
console.log('start')
var res = yield loop();
console.log('end')
}
function loop() {
console.time('loop');
for (var i = 0; i < 1000000000; i ++) {}
console.timeEnd('loop');
}
var g = generator();
g.next();
g.next();
// start
// loop: 540.537ms
// end
上面的代码中,g 是 generator 方法返回的遍历器对象。第一次调用 next() 方法时,会先执行 yield 之前的代码 TASK A,然后执行 yield 语句表达式 yield loop()
,并执行耗时函数 loop
第二次调用 next() 方法时,会等待第一个 next() 方法完成再执行 generator 方法内后面的代码
但是从上面的代码可以看出,要想用 Generator 函数实现异步编程,需要手动地来控制状态指针的后移,在实际工作中会不方便,所以就需要一个方法让 Generator 函数能够自动往后执行
基于 Promise 对象自动执行 Generator 函数
现将上面代码改造成基于 Promise 对象的 Generator 函数
function* generator() {
console.log('start')
var res = yield loop();
console.log('end')
}
function loop() {
return new Promise(function(resolve, reject) {
try {
console.time('loop');
for (var i = 0; i < 1000000000; i ++) {}
console.timeEnd('loop');
} catch (e) {
reject(e);
}
resolve();
})
}
var g = generator();
g.next().value.then(function() {
g.next();
});
// start
// loop: 2576.482ms
// end
上面代码中, loop 方法被改造成一个返回 Promise 对象的方法,这样在调用第一个 next() 方法时,返回的对象的 value 熟悉就会使一个 Promise 对象,可以使用 then() 方法,再 then() 中再次调用 next() ,亦可以达到未改动之前的异步效果
基于以上改动后的代码,将其最后执行的方法封装成可通用的代码段
function run(generator){
var g = generator();
function next(data){
var result = g.next(data);
if (result.done) return result.value;
result.value.then(function(data){
next(data);
});
}
next();
}
run(generator);
上面这段代码中的 data 是需要传递的参数,还记得在上一篇中所说的 next() 方法中参数的意义吗?戳链接直达上一篇:
需要注意的是,使用上面这段自动执行代码,yield 语句返回的 value 都必须是一个 Promise 对象,否则是无法执行的