Promise是个异步执行的过程,我们要想把多个Promise顺序执行,只能不断的利用then方法继续去执行下面的Promise,例如:
step1().then(step2,step2Failed).then(function(msg) {
return Promise.all( [
step3a( msg ),
step3b( msg ),
step3c( msg )
] )
})
.then(step4);
上面这段代码的逻辑就是step1完了,在执行step2或者step2Failed,然后在step3的三个步骤,最后step4,是个顺序执行的过程,但是Promise的语法中却不能清楚的反应出这样一个顺序,不太利于人眼阅读。
而生成器有一个特点就是可以中断-恢复函数的执行,利用这点可以yield一个Promise,接着在Promise状态fulfilled的时候,继续执行。可以把异步过程调用顺序化,看下面的代码:
function* main() {
var ret = yield step1();
try {
ret = yield step2( ret );
}
catch (err) {
ret = yield step2Failed( err );
}
ret = yield Promise.all( [
step3a(ret),
step3b(ret),
step3c(ret)]
);
yield step4( ret );
return done();
}
要想让上面的生成器执行起来,当然可以自己一个next一个next的去调用,但这样很麻烦,可以写个通用的函数:
function run(gen) {
var args = [].slice.call( arguments, 1), it;
it = gen.apply( this, args );
return Promise.resolve().then( function handleNext(value){
console.log("==>",value);
var next = it.next(value);
return (function handleResult(next){
if (next.done) {
return next.value;
}
else {//这面递归调用
return Promise.resolve(next.value).then(handleNext,function handleErr(err) {
return Promise.resolve(it.throw( err )).then( handleResult );
});
}
})( next );
});
}
具体过程函数的代码:
function step1(){
return Promise.resolve("step 1");
}
function step2(){
return Promise.resolve("step 2");
//return Promise.reject("step 2 ERROR!");
}
function step2Failed(err){
console.error(err);
return Promise.resolve("step2 failed!But We can continue.");
}
function step3a(){
return Promise.resolve("step 3a");
}
function step3b(){
return Promise.resolve("step 3b");
}
function step3c(){
return Promise.resolve("step 3c");
}
function step4(){
return new Promise(function(resolve,reject){
setTimeout(function(){
resolve("step 4");
},1000);
});
}
function done(){
return Promise.resolve("DONE!!");
}
调用run:
run( main )
.then(
function fulfilled(result){
console.log("finally:",result);
},
function rejected(reason){
}
);
最后结果:
如果在step2中用Promise.reject,可以验证step2failed过程:
是不是觉得上面的过程有点复杂,不过不要担心,在将来的ES7中会有一个async的关键字,直接加在函数前面,就能实现上面的过程。在介绍ES7的时候在解释。这里只需要记住生成器和Promise可以叠加使用,把异步过程顺序化就好了。
*以上全部代码在Chrome 47下通过测试