在JavaScript中实现协程控制流程

时间:2017-04-21 16:21:23

标签: javascript asynchronous async-await generator

以下实现了一个控制流包装器co,只能通过yield关键字来描述异步代码。

这基本上是async / await在ESwhatever中引导的内容吗?



co(function*() {
    console.log('...');
    yield one();
    console.log('...');
    yield two();
})


function co(gFn) {
    var g = gFn();

    return Promise.resolve()
        .then(go);

    function go() {        
        var result = g.next();
        if(result.done) {
            return;
        }
        if(isPromise(result.value)) {
            return result.value.then(go); // Promises block until resolution.
        }
        return Promise.resolve(result);
    }    
}

function isPromise(o) {
    return o instanceof Promise;
}

function one() {
    return new Promise(resolve => setTimeout(() => (console.log('one'), resolve()), 1000));
}

function two() {
    return new Promise(resolve => setTimeout(() => (console.log('two'), resolve()), 1000));
}




修改

根据我更新的回复来考虑回报值:



co(function*() {
    console.log('...');
    const result1 = yield one();
    console.log('result1: ', result1);
    const result2 = yield two();
    console.log('result2: ', result2);
    const result3 = yield[one(), two()];
    console.log('result3: ', result3);
    const result4 = yield{
        one: one(),
        two: two()
    };
    console.log('result4: ', result4);
})

function co(gFn) {
    var g = gFn();

    return Promise.resolve().then(go);

    function go() {
        var result = g.next(...arguments);
        if (isPromise(result.value)) {
            return result.value.then(go);
        }
        if (Array.isArray(result.value)) {
            return Promise.all(result.value).then(go);
        }
        if (isObject(result.value)) {
            var o = {};
            var promises = Object.keys(result.value).map(k=>result.value[k].then(r=>o[k] = r));
            return Promise.all(promises).then(()=>o).then(go);
        }
        return Promise.resolve(result);
    }
}

function isPromise(o) {
    return o instanceof Promise;
}

function isObject(val) {
    return val && (Object === val.constructor);
}

function one() {
    return new Promise(resolve=>setTimeout(()=>(console.log('one'),
    resolve('result 1')), 1000));
}

function two() {
    return new Promise(resolve=>setTimeout(()=>(console.log('two'),
    resolve('result 2')), 1000));
}




2 个答案:

答案 0 :(得分:4)

  

这基本上是什么async / await在ESwhatever中引起了什么?

不是真的。对于做同样的事情,这是一种不同的方法。 async/await转变为更像

async function foo() {
  const bar = await Bar();
  bar++;
  const baz = await Baz(bar);
  return baz;
}

变为

function foo() {
  return Bar()
    .then(bar => {
      bar++;
      return Baz(bar)
        .then(baz => {
          return baz;
        });
    });
}

答案 1 :(得分:3)

Stage 3 Draft / January 26, 2016 Async Functions提供了三种模式的示例; Promise; Generator; Async Functions;不同的方法基本上产生相同的结果

  

Examples#

     

采用以下示例,首先使用Promises编写。这段代码   链接元素上的一组动画,当有元素时停止   动画中的异常,并返回由...生成的值   最终成功执行的动画。

function chainAnimationsPromise(elem, animations) {
    let ret = null;
    let p = currentPromise;
    for(const anim of animations) {
        p = p.then(function(val) {
            ret = val;
            return anim(elem);
        })
    }
    return p.catch(function(e) {
        /* ignore and keep going */
    }).then(function() {
        return ret;
    });
}
     

已经使用promises,代码从直接回调样式得到了很大改进,其中这种循环和异常处理是   有挑战性的。

     

Task.js和类似的库提供了一种使用生成器进一步的方法   简化代码保持相同的含义:

function chainAnimationsGenerator(elem, animations) {
    return spawn(function*() {
        let ret = null;
        try {
            for(const anim of animations) {
                ret = yield anim(elem);
            }
        } catch(e) { /* ignore and keep going */ }
        return ret;
    });
}
     

这是一项显着的改进。除了代码的语义内容之外的所有承诺样板都被删除了   内部函数的主体表示用户意图。但是,有   一个样板的外层,用于将代码包装在一个附加的中   生成器函数并将其传递给库以转换为promise。   需要在使用它的每个函数中重复该层   产生承诺的机制。这在典型的异步中很常见   Javascript代码,有消除需要的价值   剩下的样板。

     

使用异步功能,删除所有剩余的样板,   只留下程序文本中具有语义意义的代码:

async function chainAnimationsAsync(elem, animations) {
    let ret = null;
    try {
        for(const anim of animations) {
            ret = await anim(elem);
        }
    } catch(e) { /* ignore and keep going */ }
    return ret;
}