[JavaScript] yield promise

3140 ワード

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}