【问题标题】:JavaScript asynchronous programming: promises vs generatorsJavaScript 异步编程:promise 与生成器
【发布时间】:2015-03-17 20:26:22
【问题描述】:

Promises 和生成器允许您编写异步代码。我不明白为什么在 ECMA 脚本 6 中引入了这两种机制。什么时候最好使用 Promise,什么时候最好使用生成器?

【问题讨论】:

    标签: javascript node.js asynchronous promise ecmascript-6


    【解决方案1】:

    这两种技术之间没有对立:它们共存在一起,相得益彰。

    Promises 允许您获取异步操作的结果,该操作尚不可用
    它解决了Pyramid of Doom 问题。所以而不是:

    function ourImportantFunction(callback) {
      //... some code 1
      task1(function(val1) {
        //... some code 2
        task2(val1, function(val2) {
          //... some code 3
          task3(val2, callback);
        });
      });
    }
    

    你可以写:

    function ourImportantFunction() {
      return Promise.resolve()
        .then(function() {
            //... some code 1
            return task1(val3)
        })
        .then(function(val2) {
            //... some code 2
            return task2(val2)
        })
        .then(function(val2) {
            //... some code 3
            return task3(val2);
        });
    }
    
    ourImportantFunction().then(callback);
    

    但即使使用 Promise,您也必须以异步方式编写代码 - 您必须始终将回调传递给函数。
    编写异步代码比同步代码要困难得多。即使有promise,当代码很大时,也很难看到算法(这是非常主观的,但对于大多数程序员我认为这是真的)。​​

    所以我们想以同步方式编写异步代码。这就是 generators 来帮助我们的地方。
    代替上面的代码,你可以写:

    var ourImportantFunction = spawn(function*() {
        //... some code 1
        var val1 = yield task1();
        //... some code 2
        var val2 = yield task2(val1);
        //... some code 3
        var val3 = yield task3(val2);
    
        return val3;
    });
    
    ourImportantFunction().then(callback);
    

    最简单的spawn 实现可能是这样的:

    function spawn(generator) {
      return function() {    
        var iter = generator.apply(this, arguments);
    
        return Promise.resolve().then(function onValue(lastValue){
          var result = iter.next(lastValue); 
    
          var done  = result.done;
          var value = result.value;
    
          if (done) return value; // generator done, resolve promise
          return Promise.resolve(value).then(onValue, iter.throw.bind(iter)); // repeat
        });
      };
    }
    

    如你所见value(一些异步函数task{N}的结果)必须是一个promise。回调无法做到这一点。

    剩下要做的就是在语言本身中实现spawn 技术。因此,我们将spawn 替换为async,将yield 替换为await,并将来到ES7 async/await

    var ourImportantFunction = async function() {
        //... some code 1
        var val1 = await task1();
        //... some code 2
        var val2 = await task2(val1);
        //... some code 3
        var val3 = await task3(val2);
    
        return val3;
    }
    

    我建议你观看this video 以了解更多关于这个和其他一些即将到来的技巧。
    提示:如果这个人对你说话太快,请放慢播放速度(“设置”在右下角,或者只需按下 [shift + ])

    什么是最好的:只有回调,或承诺,或带有生成器的承诺 - 这是一个非常主观的问题。
    回调是目前可能最快的解决方案(本机 Promise 的性能现在非常糟糕)。使用生成器的 Promise 让您有机会以同步方式编写异步代码。但是现在它们比简单的回调要慢得多。

    【讨论】:

    • @BenjaminGruenbaum 好的,谢谢您的发言。这很有帮助。
    • @Roamer-1888 谢谢!现在好了。
    • @Roamer-1888 我认为今天你应该得到“寻虫者”徽章,如果是这样的话)
    • 是的,本杰明知道,我有时会触怒他人。事实是,我只关心值得付出努力的答案。
    • @Roamer-1888 我添加了关于减慢播放速度的评论。也许它会对某人有所帮助。
    【解决方案2】:

    Promises 和 Generators 是不同的软件模式(构造):

    1. http://en.wikipedia.org/wiki/Futures_and_promises
    2. http://en.wikipedia.org/wiki/Generator_(computer_programming)

    事实上,生成器不是异步的。

    当您需要获取一系列值而不是一次获取一系列值时,生成器非常有用,而是根据需求获取一个值。生成器将在每次调用时立即(同步)返回下一个值,直到它到达序列的末尾(或者在无限系列的情况下是无限的)。

    当您需要“延迟”可能尚未计算(或可能不可用)的值时,Promise 很有用。当值可用时 - 它是整个值(不是它的一部分),即使它是一个数组或其他复杂值。

    您可以在维基百科文章中查看更多详细信息和示例。

    【讨论】:

      猜你喜欢
      • 2015-08-08
      • 2019-02-14
      • 1970-01-01
      • 2021-11-12
      • 2018-01-14
      • 2017-01-09
      • 2010-10-24
      • 2019-05-19
      相关资源
      最近更新 更多