Javascript ES6生成器和函数数组之间的差异

Javascript ES6生成器和函数数组之间的差异,javascript,sequence-generators,Javascript,Sequence Generators,在阅读javascript博客和文章时,我看到很多人对ES6生成器感兴趣,但我无法理解它们在本质上与当前由一系列函数组成的序列有何不同。例如,下面的工厂将采取一系列的功能步骤,并在步骤之间实现产量 function fakeGen(funcList) { var i = 0, context; return function next() { if (i<funcList.lenght) { return {value: funcLi

在阅读javascript博客和文章时,我看到很多人对ES6生成器感兴趣,但我无法理解它们在本质上与当前由一系列函数组成的序列有何不同。例如,下面的工厂将采取一系列的功能步骤,并在步骤之间实现产量

function fakeGen(funcList) {
    var i = 0, context;
    return function next() {
        if (i<funcList.lenght) {
            return {value: funcList[i++](context)}
        } else return {done:true}
    }
}
函数fakeGen(函数列表){
var i=0,上下文;
返回函数next(){

如果(i生成器本质上是一个枚举器函数,它允许在调用它时更改您操作的上下文,实际上它与您的函数数组之间没有很大的区别,但是您得到的优势是它不必是正在求值的函数中的函数,simplif以下是一个例子:

function* myGenerator() {
    for (var i = 0; i < arr.length; i++) {
        yield arr[i];
    }
}
使用链式嵌套函数可以完成同样的事情,但是代码的可维护性和可读性会受到一定的影响

就传输程序而言,从CS线程:

没有冲突。Coffeescript只会生成编译它使用的任何语法所需的javascript,不管是旧的还是新的。
在过去,coffeescript在所有浏览器都支持之前不会使用任何javascript功能。这可能也适用于生成器。在此之前,您需要使用backticks

我对大多数Transpiler的一般理解是,它们在实现不会向后遍历且通常兼容的功能时必须小心,因此通常在游戏后期

正如您所说,生成器并没有做任何特别的事情,它只是语法上的糖分,使编码更容易阅读、维护、使用或执行得更好。

是正确的。您可以在ES3/ES5中完全实现相同的功能。但不是相同的语法。让我们举一个例子来解释语法的重要性

ES6生成器的主要应用之一是异步操作。有一种设计用于封装生成器,生成一系列的。当封装的生成器产生承诺时,这些运行程序等待该承诺得到解决或拒绝,然后恢复生成器,将结果传回或在产生点抛出异常使用
迭代器.throw()
进行int运算

一些运行程序,例如,还允许生成承诺数组,并传回值数组

下面是一个示例。此函数并行执行两个url请求,然后将其结果解析为JSON,以某种方式组合,将组合数据发送到其他url,并返回(承诺)答案:

请注意,此代码几乎不包含样板文件。大多数代码行执行上述描述中存在的某些操作

还要注意缺少错误处理代码。所有的错误,无论是同步的(比如JSON解析错误)还是异步的(比如失败的url请求),都会被自动处理,并且会拒绝最终的承诺

如果您需要从某些错误中恢复(即防止它们拒绝产生的承诺),或者使它们更具体,那么您可以使用
try..catch
,将生成器中的任何代码块包围起来,同步和异步错误都将在
catch
块中结束

使用一系列函数和一些助手库(如:

但这真的很难看。更好的办法是使用承诺:

var createSmth = function(id) {
  var entity;
  return Promise.all([
    request.get('http://some.url/' + id),
    request.get('http://other.url/' + id)
  ])
  .then(function(results) {
    var jsons = results.map(JSON.parse);
    entity = { x: jsons[0].meta, y: jsons[1].data };
    return request.post('http://third.url/' + id, JSON.stringify(entity));
  })
  .then(function(answer) {
    return { entity: entity, answer: JSON.parse(answer) };
  });
};

createSmth('123').then(consumeResult).catch(handleError);
与使用生成器的版本相比,代码更短、更干净,但仍然更多。还有一些样板代码。请注意这些
。然后(function(…){
行和
var entity
声明:它们不执行任何有意义的操作

更少的样板(=生成器)使您的代码更易于理解和修改,编写起来也更有趣。这些是任何代码最重要的特征之一。这就是为什么许多人,特别是那些习惯了其他语言中类似概念的人,对生成器如此着迷的原因:)

关于您的第二个问题:Transpiler使用闭包、
switch
语句和状态对象来实现其thanspiling魔力。例如,此函数:

function* f() {
  var a = yield 'x';
  var b = yield 'y';
}
将由转换为此(的输出看起来非常相似):

正如您所见,这里没有什么神奇之处,生成的ES5是相当平凡的。真正的神奇之处在于生成生成ES5的代码,也就是说,在Transpiler的代码中,因为它们需要支持所有可能的边缘情况。最好以一种能产生高性能输出代码的方式来实现


UPD:可追溯到2000年,描述了在普通C中实现伪协同路由:)Regenerator和其他ES6>ES5 Transpiler用于捕获生成器状态的技术非常相似。

更多的是从代码中看不到的内容,而不是内部优化。感谢您提供了详细的回答。我从中获得了错误处理和更清晰的语法。是的,但这是很多:)@Hurelu,我正在浏览我的书签,发现了一篇与你的第二个问题相关的文章。添加到了答案中(见下方的UPD)。谢谢。另一个收获是错误处理部分。我仍然在思考它。
var createSmth = function(id, cb) {
  var entity;
  async.series([
    function(cb) {
      async.parallel([
        function(cb){ request.get('http://some.url/' + id, cb) },
        function(cb){ request.get('http://other.url/' + id, cb) }
      ], cb);
    },
    function(results, cb) {
      var jsons = results.map(JSON.parse);
      entity = { x: jsons[0].meta, y: jsons[1].data };
      request.post('http://third.url/' + id, JSON.stringify(entity), cb);
    },
    function(answer, cb) {
      cb(null, { entity: entity, answer: JSON.parse(answer) });
    }
  ], cb);
};

createSmth('123', function(err, answer) {
  if (err)
    return handleError(err);
  consumeResult(answer);
});
var createSmth = function(id) {
  var entity;
  return Promise.all([
    request.get('http://some.url/' + id),
    request.get('http://other.url/' + id)
  ])
  .then(function(results) {
    var jsons = results.map(JSON.parse);
    entity = { x: jsons[0].meta, y: jsons[1].data };
    return request.post('http://third.url/' + id, JSON.stringify(entity));
  })
  .then(function(answer) {
    return { entity: entity, answer: JSON.parse(answer) };
  });
};

createSmth('123').then(consumeResult).catch(handleError);
function* f() {
  var a = yield 'x';
  var b = yield 'y';
}
var f = regeneratorRuntime.mark(function f() {
  var a, b;
  return regeneratorRuntime.wrap(function f$(context$1$0) {
    while (1) switch (context$1$0.prev = context$1$0.next) {
      case 0:
        context$1$0.next = 2;
        return "x";
      case 2:
        a = context$1$0.sent;
        context$1$0.next = 5;
        return "y";
      case 5:
        b = context$1$0.sent;
      case 6:
      case "end":
        return context$1$0.stop();
    }
  }, f, this);
});