I'm attempting to chain multiple getItem
promises from the LocalForage library, the keys are read from an Array.
PROBLEM: I need the Resolve or Reject callback to trigger AFTER all the LocalForage promises are finished.
Both methods didn't have the right callstack. Any ideas?
CODE 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);
}
CODE 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);
}
I need the Resolve or Reject callback to trigger AFTER all the LocalForage promises are finished.
Yes. lf.getItem
is asynchronous, but you are testing for (result.resolved + result.rejects) == keyHandlerPairs.length
right after having fired all calls. Both .resolved
and .rejects
will still be 0
(notice that you never increment the number of rejects anyway).
First, you should not use the Promise
constructor other than to construct a promise for a single asynchronous API that does not yet support promises. There should no logic go into it, just a plain call. Let's assume you wanted to use Promise.all
to wait for all your concurrently running promises and get the combined results from them as a promise for an array. Your code should look like this:
if (!$.isArray(keyHandlerPairs))
keyHandlerPairs = [keyHandlerPairs];
var promises = keyHandlerPairs.map(function(kv) {
return lf.getItem(kv.key, kv.handler);
});
return Promise.all(promises);
OK, now assume you actually don't care about the results and whether all promises succeeded, but only about the numbers of fulfilled vs rejected promises. OK, there is no native combinator function for this, so we need to write our own (and use the Promise
constructor). However, this functionality should be generic and have nothing to do with key-value handler pairs.
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);
}
});
};
Yes, it isn't as trivial as it looks at first (OK, maybe above version is a bit too detailed). But once you have this, you can simply replace .all()
in the first snippet by .countResolutions()
and you get the expected result.