【问题标题】:Outer variables not being altered inside Promise.then function在 Promise.then 函数中未更改外部变量
【发布时间】:2015-10-18 20:44:08
【问题描述】:

在节点上使用 thinky.js 时,我试图遍历一个循环并将每个项目添加到一个数组中。但是,由于某种原因,这不起作用。

在另一个地方,它是相同的并且可以工作,只是没有 Promise.then 函数。为什么这不起作用?

var fixedItems = [];
for (i in tradeItems) {
  var item = tradeItems[i];
  Item.get(item["id"]).run().then(function(result) {
    var f = { "assetid": result["asset_id"] };
    console.log(f);  // WOrks
    fixedItems.push(f); // Doesn't work
  });
}

console.log(fixedItems); // Nothing

【问题讨论】:

  • 如果 Item.get(item["id"]).run() 是异步的,那么您在固定项目开始执行之前就记录了它们
  • In another place, it is indentical and working - 你能显示这个相同的工作代码吗?

标签: javascript node.js promise thinky


【解决方案1】:

Promise 表示任务的未来结果。在这种情况下,您在任务(对Item.get 的调用)完成工作之前记录了fixedItems。换句话说,then 函数还没有运行,所以没有任何东西被放入 fixedItems

如果你想在 fixedItems 包含所有项目后使用它,你需要等待所有的 Promise 解决。

如何做到这一点取决于您使用的 Promise 库。这个带有Promise.all 的示例适用于许多库,包括原生 ES6 Promises:

// Get an array of Promises, one per item to fetch.
// The Item.get calls start running in parallel immediately.
var promises = Object.keys(tradeItems).map(function(key) {
  var tradeItem = tradeItems[key];
  return Item.get(tradeItem.id);
});

// Wait for all of the promises to resolve. When they do,
//  work on all of the resolved values together.
Promise.all(promises)
  .then(function(results) {
    // At this point all of your promises have resolved.
    // results is an array of all of the resolved values.

    // Create your fixed items and return to make them available
    //  to future Promises in this chain
    return results.map(function(result) {
      return { assetid: result.asset_id }
    });
  })
  .then(function(fixedItems) {
    // In this example, all we want to do is log them
    console.log(fixedItems);
  });

推荐阅读:HTML5 rocks intro to Promises

【讨论】:

  • 这是我回答的一个很好的替代方法。但我不认为Promise.all() 并行执行承诺(我错了吗?)如果有一种方法可以并行执行它们而不是顺序执行它们可能会更有效。这是一个相当小的问题(并且可能是过早的优化),但我很想知道是否有一种简单的方法可以使用本机 Promise 进行并行执行。
  • 你错了——它们是并行运行的。事实上,当您调用Promise.all 时,它们已经全部并行运行。一个 Promise 总是代表一个 running 任务,所以如果你有一个 Promise,它的任务就在进行中。事实上,使用 Promises 串行执行需要更多的努力。
  • 啊,我现在明白了(当然,map 函数是同步运行的,所以 Promise 立即开始执行......)。使用Promise.map 怎么样?您链接的文章没有提到它,但这也可以在这里工作,不是吗? (与Promise.all 相比没有什么特别的优势,只是想我会提一下)。
  • 您也可以使用Promise.map 来支持支持它的库。我选择了Promise.all,因为它是ES6 Promise API 的一部分,它比Bluebird 和Q 等许多库要小很多。
【解决方案2】:

您的问题是您在循环中的任何承诺完成执行之前调用console.log(fixedItems)。一个更好的解决异步问题的方法是首先将所有项目 ID 放入一个数组中,然后在单个查询中检索所有项目,这在数据库端也更有效。

var itemIds = tradeItems.map(function(item) {
    return item.id;
});

var fixedItems = [];

//you would need to write your own getItemsById() function or put the code
//to get the items here
getItemsById(itemIds).then(function(items) {

    items.forEach(function(item) {
        var f = { "assetid": result["asset_id"] };
        fixedItems.push(f);
    });

    whenDone();
});

function whenDone() {
    //you should be able to access fixedItems here
}

我无法轻松找到如何使用 thinky 在单个查询中按 ID 查找多条记录,但我确实找到了这个页面,它可能会有所帮助: http://c2journal.com/2013/01/17/rethinkdb-filtering-for-multiple-ids/

虽然这将是我解决此问题的首选方式,但仍然可以使用多个查询并使用承诺链等待它们全部解决,然后再继续执行后续代码。如果您想走这条路,请查看此链接:http://promise-nuggets.github.io/articles/11-doing-things-in-parallel.html。 (注意:我没有亲自使用过 Bluebird,但我认为该链接中的 Bluebird 示例可能已过时。map 方法似乎是当前推荐使用 promise 执行此操作的方法:https://stackoverflow.com/a/28167340/560114。)

更新:或者对于后一种选择,您可以使用上面 joews 答案中的代码。

【讨论】:

    猜你喜欢
    • 2017-03-08
    • 1970-01-01
    • 2011-06-24
    • 2019-02-19
    • 2021-10-12
    • 1970-01-01
    • 1970-01-01
    • 2017-05-07
    • 1970-01-01
    相关资源
    最近更新 更多