I'm trying to figure out how promises work and I've understood a basic concept but I don`t really get it how should I wait for multiple promises to finish. Especially since they are created dynamically, one after the other.
I'd be happy if someone could point me in a direction for a more elegant solution.
In short, what I need is:
- Go through an array of objects, (iteration)
- Check does it exist in DB* (async)
- If it exists, update data in DB* (async)
- If it doesn`t exist, add new object to the DB* (async)
- Once all of this is done, do something else
*I'm using localforage in my app but I'm simulating it with setTimeout.
I've thought of using this approach to sequence iteration: JavaScript ES6 promise for loop
for (let i = 0, p = Promise.resolve(); i < 10; i++) {
p = p.then(_ => new Promise(resolve =>
setTimeout(function () {
console.log(i);
resolve();
}, Math.random() * 1000)
));
}
It's tricky because I don`t have all the promises created when this iteration completes, so I need to keep track of them (promises) separately and run "Promise.allSettled" only once they are.
Here is my test fiddle https://jsfiddle.net/mefistofelos/2ergotvw/
Entire code here:
let app = {
"data": {
"arr" : [5,10,15,20,25,30,35,"1.oni"],
"ids" : [1,2/*,3,4,5,6,7,8,9,10*/,100],
"items" : [{"id": 1, "name": "apple"},{"id": 2, "name": "pear"},{"id": 3, "name": "plum"},{"id": 4, "name": "ananas"},{"id": 17, "name": "brick"}],
getItem : (id, ptIndex) => new Promise(
resolve => setTimeout(
() => {
let resp = {"id": id, "data": [], "ptIndex" : ptIndex};
resp.data = app.data.items.filter( (ob) => ob.id == id );
console.log("id: " + id + " ptIndex: " + ptIndex, resp);
resolve(resp)
}
, Math.floor((Math.random() * 1500) + 300))
),
setItem : (data) => new Promise(
(resolve) => setTimeout(
() => {
let resp = data;
resp.status = "success";
app.promisesSequence._items.push(data);
console.log("setItem Resp Data", data);
return resolve(resp);
}
, Math.floor((Math.random() * 2500) + 800))
)
},
// build a set of anonymous functions and run them in sequence
"promisesSequence": {
// holds promise functions
"_items" : [],
/*"_track" : [],*/
"_promiseArr" : [],
// initiate promise tracking
"track" : {
"multiplier" : 2,
"data" : [],
start : (total) => {
app.promisesSequence.track.data.push({"total": total,"arr":[]});
return app.promisesSequence.track.data.length-1;
},
// add promise to the promise tracking tree
addPromise : (ptIndex, promise) => {
app.promisesSequence.track.data[ptIndex].arr.push(promise);
return promise;
},
isReady : (ptIndex) => (app.promisesSequence.track.getTotal(ptIndex) == app.promisesSequence.track.getTracked(ptIndex) ),
getTotal : (ptIndex) => app.promisesSequence.track.data[ptIndex].total*app.promisesSequence.track.multiplier,
getTracked : (ptIndex) => app.promisesSequence.track.data[ptIndex].arr.length,
getPromises : (ptIndex) => app.promisesSequence.track.data[ptIndex].arr,
},
onFinishCb: _ => {
console.log("[onFinishCb]: Script finished, ("+app.promisesSequence._items.length+") items added: ", app.promisesSequence._items);
},
// build promise function array
build: _ => {
console.log("init promisesSequence");
// start tracking items
let ptIndex = app.promisesSequence.track.start(app.data.ids.length);
console.log("ptIndex: " + ptIndex);
// go through array
for (let i = 0, p = Promise.resolve(); i <= app.data.ids.length-1; i++) {
console.log("get Item " +app.data.ids[i]+" Data: " );
p = p.then( _ => {
app.promisesSequence.track.addPromise(ptIndex,app.data.getItem(app.data.ids[i], ptIndex)).then(
(resp) => {
console.log("GOT item (" + resp.id + ") data: ", resp.data);
if ( resp.data.length >= 1 ) {
item = resp.data[resp.data.length-1];
item.edited = true;
item.a = "edit";
} else {
item = {"id": resp.id, "data": [], "name" : "", "a" : "insert"};
}
// pass promiseTreeIndex
item.ptIndex = resp.ptIndex;
console.log("Try to Set item ("+item.id+"): ",item);
app.promisesSequence.track.addPromise(item.ptIndex, app.data.setItem(item)).then (
(resp) => console.log("Item ("+resp.id+") is Set.", resp)
)
// try to bind CB after the last of the GET requests have been created
if ( app.promisesSequence.track.isReady(item.ptIndex) === true ) {
console.log("**Tracked Promises: " + app.promisesSequence.track.getTracked(item.ptIndex) + " From Total of " + app.promisesSequence.track.getTotal(item.ptIndex));
console.log("Call Promise.allSettled");
Promise.allSettled(app.promisesSequence.track.getPromises(item.ptIndex)).then(app.promisesSequence.onFinishCb);
}
return i;
})
})
}
console.log("iteration complete");
},
run: () => {
return app.promisesSequence.build();
}
}
}
app.promisesSequence.run();
As you can see, it is sequentially adding Items and I have proper data in both "GET" and "SET" functions, so I've figured out those.
My Question: Is there a more professional way of doing this, than the one I came up with? I was hoping to avoid tracking every promise I create. By going one step further I could have manually checked if they are done so I wouldn`t even need to use .allSettled method.