【问题标题】:Implementing coroutine control flow in JavaScript在 JavaScript 中实现协程控制流
【发布时间】:2017-09-18 17:53:18
【问题描述】:

以下实现了一个控制流包装器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));
}

【问题讨论】:

  • “这基本上是async/await 在 ESwhatever 中所做的事情吗?” 调查仅仅是为了确定一种方法与另一种方法的等效性吗?
  • 你有没有看过任何转译器的秘密,看看他们是如何实现的?
  • @PhilCooper 我看过co source。
  • @guest271314 是的。
  • 是的,您已经掌握了基本概念。但是a)你将错误的参数传递给goFn b)你的go函数没有return result.valuec)你从来没有处理过任何承诺错误(这应该导致g.throw(…))d)await确实不专门处理数组或对象……还有一些更小的东西。

标签: javascript asynchronous async-await generator


【解决方案1】:

这基本上是 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;
        });
    });
}

【讨论】:

  • @Bergi 但是您在编辑中添加的代码 (.then(baz => { return baz; })) 是无效的。
  • const baz = …; return baz; 也是如此 :-) Apart from the additional resolve step it's useless indeed,但async/await 的翻译是机械的,你在代码中放置了一个无用的await,很好…
  • @torazaburo,嘿,您能否也请扩展您的答案以包括如何将稍微修改的示例转换为承诺方法?这是修改后的示例:const bar = await Bar(); someAsyncFn(); bar++;...。它只是与bar++; 一起执行,对承诺链没有任何影响吗?
  • @Maximus 是的,这只是一个标准的函数调用。
  • @Bergi,我明白了,谢谢。所以我可以假设对没有await 的函数的任何调用都不会将.then 添加到原型链中?
【解决方案2】:

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;
    });
}

已经有了 Promise,代码比直接的回调风格有了很大的改进,这种循环和异常处理是 具有挑战性。

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;
}

【讨论】:

    猜你喜欢
    • 2014-02-26
    • 1970-01-01
    • 2019-04-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多