Цепочки обещаний JavaScript от LocalForage в цикле for
Я пытаюсь связать несколько getItem
обещания из библиотеки LocalForage, ключи читаются из массива.
ПРОБЛЕМА. Мне нужен обратный вызов Resolve или Reject для запуска ПОСЛЕ того, как все обещания LocalForage завершены.
Оба метода не имели правильного стека вызовов. Есть идеи?
КОД 1:
function load(keyHandlerPairs, correct, incorrect) {
var result = {
resolved: 0,
rejects: 0,
},
p = new Promise(function (resolve, reject) {
//Retrieve objects from localstorage;
if ($.isArray(keyHandlerPairs)) {
for (var i = 0; i < keyHandlerPairs.length; i++) {
var kv = keyHandlerPairs[i];
lf.getItem(kv.key, kv.handler).then(
//Resolved
function () { console.log('resolved'); result.resolved++; }
);
}
} else {
var kv = keyHandlerPairs;
lf.getItem(kv.key, kv.handler);
}
if ((result.resolved + result.rejects) == keyHandlerPairs.length) {
console.log(result.resolved, result.rejects, keyHandlerPairs.length);
resolve(result);
} else {
console.log(result.resolved, result.rejects, keyHandlerPairs.length);
reject(result);
}
}).then(correct, incorrect);
}
КОД alt:
if ($.isArray(keyHandlerPairs)) {
var promises = [];
for (var i = 0; i < keyHandlerPairs.length; i++) {
var kv = keyHandlerPairs[i];
promises.push(lf.getItem(kv.key, kv.handler));
}
Promise
.all(promises)
.then(function (value) { console.log(value); result.resolved++; })
.catch(function (error) { console.log(error); result.rejects++; });
} else {
var kv = keyHandlerPairs;
lf.getItem(kv.key, kv.handler);
}
1 ответ
Мне нужен обратный вызов Resolve или Reject для запуска ПОСЛЕ того, как все обещания LocalForage завершены.
Да. lf.getItem
асинхронный, но вы проверяете (result.resolved + result.rejects) == keyHandlerPairs.length
сразу после всех звонков. И то и другое .resolved
а также .rejects
все еще будет 0
(обратите внимание, что вы никогда не увеличиваете количество отклонений в любом случае).
Во-первых, вы не должны использовать Promise
конструктор, отличный от создания обещания для одного асинхронного API, который еще не поддерживает обещания. Там не должно быть никакой логики, просто вызов. Давайте предположим, что вы хотели использовать Promise.all
ждать всех ваших одновременно выполняемых обещаний и получать объединенные результаты от них как обещание для массива. Ваш код должен выглядеть так:
if (!$.isArray(keyHandlerPairs))
keyHandlerPairs = [keyHandlerPairs];
var promises = keyHandlerPairs.map(function(kv) {
return lf.getItem(kv.key, kv.handler);
});
return Promise.all(promises);
Хорошо, теперь предположим, что вы на самом деле не заботитесь о результатах и о том, все ли обещания были выполнены, а только о количестве выполненных и отклоненных обещаний. ОК, для этого нет встроенной функции комбинатора, поэтому нам нужно написать свою собственную (и использовать Promise
конструктор). Однако эта функциональность должна быть общей и не иметь ничего общего с парами обработчиков ключ-значение.
Promise.countResolutions = function(promises) {
var result = {fulfillments:0, rejections:0},
len = 0;
return new Promise(function(resolve, reject) {
function addResult(type) {
result[type]++;
if (result.fulfillments+result.rejections == len)
resolve(result);
return true;
}
try {
let i = 0;
for (let promise of promises) {
let called = false;
i++;
promise.then(function onfulfilled() {
if (!called) called = addResult("fulfillments");
}, function onrejected() {
if (!called) called = addResult("rejections");
});
}
len = i;
if (result.fulfillments+result.rejections == len)
resolve(result);
} catch (e) {
reject(e);
}
});
};
Да, это не так тривиально, как кажется на первый взгляд (хорошо, возможно, вышеприведенная версия слишком подробна). Но когда у вас есть это, вы можете просто заменить .all()
в первом фрагменте .countResolutions()
и вы получите ожидаемый результат.