当Generator和Promise混合使用的时候,会产生神奇的效果,
可以用同步的方式书写异步的代码。
//1. 定义spawn
function spawn(generatorFunc){
function continuer(verb,arg){
var result;
try{
//6. 调用iterator['next'](undefined),即iterator.next(undefined);
//8. yield返回一个{done:true/false,value:promise},
//所以result.value的值就是yield表达式后面那个promise了
//12. 调用iterator['next'](promise的结果值),会导致生成器中的代码继续执行
//并把next的参数值当做yield的返回值
result=iterator[verb](arg);
}catch(err){
return Promise.reject(err);
}
//9. 判断generator是否已执行完,如果执行完返回generator的return值。
if(result.done){
return result.value;
}else{
//10. 返回一个新的promise作为continuer->onFulfilled->spawn的返回值
//这时候主线程执行完了,等待task queue中有promise fulfilled
//……
//11. promise fulfilled了,会拿promise的结果值,调用onFulfilled函数
//相当于用'next'和"promise的结果值"调用continuer
return Promise.resolve(result.value).then(onFulfilled,onRejected);
}
}
//3. 生成一个迭代器,这时生成器中的代码还未执行
var iterator=generatorFunc();
//4. 返回一个新函数
//执行结果相当于让continuer的this指向continuer,第一个参数为'next'
var onFulfilled=continuer.bind(continuer,'next');
var onRejected=continuer.bind(continuer,'throw');
//5. 相当于以'next'为参数调用continuer,其中arg===undefined
return onFulfilled();
}
//2. 用一个生成器作为参数调用spawn
spawn(function*(){
try{
//7. 调用iterator.next才开始执行生成器中的代码,执行到yield后返回
//其中getJSON的返回值是一个promise
//13. yield的返回值就是"promise的结果值"了
//把"promise的结果值"赋值给story,完成了用同步代码实现异步功能
let story=yield getJSON('story.json');
addHtmlToPage(story.heading);
let chapterPromises=story.chapterUrls.map(getJSON);
for(let chapterPromise of chapterPromises){
let chapter=yield chapterPromise;
addHtmlToPage(chapter.html);
}
addTextToPage('All done');
}catch(err){
addTextToPage('Argh, broken: '+err.message);
}
document.querySelector('.spinner').style.display='none';
});
注:
(1)yield v;
不会把v直接返回给iterator.next()
,
而是返回{done:true/false,value:v}
。
var generator=function*(){
yield 1;
return 2;
}
var iterator=generator();
iterator.next(); //{done:false,vaue:1}
iterator.next(); //{dont:true,value:2}
iterator.next(); //{dont:true,value:undefined}
iterator.next(); //{dont:true,value:undefined}
(2)调用iterator.next
,可以传值,作为yield
的返回值
function* gen(){
var v1=yield 1;
console.info(v1);//2
var v2=yield 2;
console.info(v2); //3
return 0;
}
variter=gen();
var t1=iter.next(1);
console.warn(t1); //Object {value: 1, done: false}
var t2=iter.next(2); //2
console.warn(t2); //Object {value: 2, done: false}
var t3=iter.next(3); //3
console.warn(t3); //Object {value: 0, done: true}