JavaScript 异步编程:promises 与生成器

JavaScript asynchronous programming: promises vs generators

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

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

Promises 允许您获得 yet.
不可用的异步操作的结果 它解决了 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);

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

所以我们想以同步方式编写异步代码。这就是 生成器 来帮助我们的地方。
除了上面的代码,您还可以这样写:

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})必须是一个承诺。你不能用回调来做到这一点。

剩下要做的就是将 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 + <])

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

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

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

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

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

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

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