Внешние переменные не изменяются внутри функции 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 выше.