【问题标题】:Fixing scope/context mistake in Node.js with Bluebird promises使用 Bluebird Promise 修复 Node.js 中的范围/上下文错误
【发布时间】:2016-12-10 23:05:04
【问题描述】:

感谢this awesome video playlist,我以为我终于掌握了一些基本的 javascript 概念:回调、范围和上下文不再是难题。

然后,我决定使用 Promise(Node.js 中的 bluebird)......这些问题再次出现,我想我不明白范围和上下文如何与 Promise 一起工作。这是我运行的测试:

function testit(){
  for (var i=0 ; i<3 ; i++) {
    var test = i;
    console.log(test);
    
    Promise
    .delay(5000) // do some async operations...
    .then( () => {
        console.log(test);
    });
  }
  return("finished");
}
console.log(testit());

//// What I hoped to see :
// 0, 1, 2, finished, 0, 1, 2

//// What happens :
// 0, 1, 2, finished, 2, 2, 2

我在这里,回到范围/上下文问题。为了解决这个问题,我找到了 bluebird Promise.bind(),它给了我:

function testit(){
  for (var i=0 ; i<3 ; i++) {
    var test = i;
    console.log(test);
    
    Promise
    .bind(this, test)
    .delay(5000)
    .then( (test) => {
        console.log(test);
    });
  }
  return("finished");
}
console.log(testit());

//// What happens :
// 0, 1, 2, finished, 0, 1, 2

耶!我有一个解决方法!但是在链接.next() 方法时显然不是很方便:我必须将test 的值从一个承诺传递给另一个承诺......在我的代码中不能这样做,我在其中链接了许多不同的函数/方法.

那么,在 Node.js 中使用 Promise 时,是否有一种更简洁的方法来“绑定”/“保留一些值”来自 for 循环?


编辑:我正在考虑关闭循环内的承诺链:

function testit(){
  for (var i=0 ; i<3 ; i++) {
    var test = i;
    console.log(test);
    
    (function (test){
      Promise
      .delay(5000) // do some async operations...
      .then( () => {
          console.log(test);
      });
    })(test);
  }
  return("finished");
}
console.log(testit());

//// What happens :
// 0, 1, 2, finished, 0, 1, 2

它也可以,这是我迄今为止最好的代码。但我最初的问题仍然存在:是否有更好/更清洁的方法来实现最初预期的结果?


结论:接受的答案可能是管理循环内异步操作的最干净的方法。使用在循环之外声明的闭包函数。谢谢 Rob M.!

【问题讨论】:

    标签: javascript node.js promise bluebird


    【解决方案1】:

    闭包是您最好的选择,否则test 将绑定到父范围(不是Promise 回调的范围),您将继续看到test 的运行时间值,而不是调用时间你想要的价值。

    function getPromise(index) {
       return Promise
        .delay(5000)
        .resolve('index: ')
        .then((prefix) => {
            console.log(prefix + index);
        });
    }
    
    function testit(){
      for (var i=0 ; i<3 ; i++) {
        var test = i;
        console.log(test);
        getPromise(i);
      }
      console.log('finished');
    }
    

    .bind 是一种完全可以接受的方法,但如果它对你更有效

    【讨论】:

    • 感谢这个答案,我只是想了一下(看看我的问题中的编辑)!我明白为什么以及如何,但在这种情况下我不能避免关闭吗?如果是这样,我猜任何包含异步操作的 for 循环都必须关闭?
    • 闭包确实是强制性的,没有更好的方法可以做到这一点。 test 是在 Promise 上方的范围内定义和设置的,并且由于您的代码是异步的并且没有将变量捕获到自己的范围内,因此它使用的是运行时间值,而不是调用时间值
    • 哦,我想知道闭包函数是否存在效率问题:我在循环内部声明了一个匿名闭包,而您在外部声明并命名它。我写它的方式对我来说更具可读性(“好的,所以我只是将这些东西包装在一个闭包中”),但我想你的方式只需要一个函数声明就更有效了?
    • 你是对的,定义一个外部函数比定义n个内部函数或使用绑定更有效。虽然我同意内联操作更具可读性,但 cmets 和命名有望最大程度地减少混淆
    • 我很高兴,不用担心异步的 Promise 与 Closure,这就是你想要弄清楚的! (希望这会有所帮助)
    猜你喜欢
    • 1970-01-01
    • 2014-05-10
    • 2015-03-04
    • 1970-01-01
    • 2020-06-16
    • 2020-02-21
    • 1970-01-01
    • 2015-07-05
    • 1970-01-01
    相关资源
    最近更新 更多