Внешние переменные не изменяются внутри функции Promise.then

На узле при использовании 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

2 ответа

Обещание представляет будущий результат задачи. В этом случае вы входите fixedItems перед вашими задачами (звонки Item.get) закончил работу. Другими словами, then функции еще не запущены, поэтому ничего не было введено в fixedItems,

Если вы хотите использовать fixedItems как только он будет содержать все элементы, вам нужно будет дождаться выполнения всех обещаний.

Как это сделать, зависит от используемой вами библиотеки Promise. Этот пример с Promise.all, работает со многими библиотеками, включая собственные обещания ES6:

// 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 знакомит с Обещаниями.

Ваша проблема в том, что вы звоните console.log(fixedItems) прежде чем любое из обещаний в цикле завершится. Лучший способ сделать это, что также решило бы асинхронную проблему, состоит в том, чтобы сначала поместить все идентификаторы элементов в массив и извлечь все элементы в одном запросе, что также более эффективно на стороне базы данных.

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, но я нашел эту страницу, которая может помочь: 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 метод, по-видимому, является текущим рекомендуемым способом сделать это с обещаниями: /questions/23965624/kak-sdelat-parallelnyie-asinhronnyie-neskolko-zaprosov-odnovremenno-s-promises-v-node/23965652#23965652.)

Обновление: или для этого последнего варианта, вы можете просто использовать код в ответе joews выше.

Другие вопросы по тегам