I'm currently working on a rather simple logic for processing queued ZPL print jobs, which are stored in an array that's then being iterated sending n amount of copies per job to a printer.
I'm reducing the array into a promise chain mixing-in a sub-chain for each job which sends the copies to the printer. The calls to the printer are synchronous (ye I know...) so I wrapped each one of them into a Promise that only resolves when the printer received the copy, thus ensuring sequential processing.
In case of a failed transmission the current promise rejects with a hand-crafted error which is being caught in the main-chain.
So far the theory, alas there seems to be a kind of race-condition between the sub-chains.
I tried my best, but I simply don't see it...
Here some simplified code + fiddle, notice how the sub-chains are not running subsequential:
['job1', 'job2'].reduce((pMain, item, curIndex) => {
var pSub = Promise.resolve();
for (let i = 0; i < 2; i++) pSub = pSub.then(() => new Promise((resolve, reject) => setTimeout(reject, 2000)));
return pMain.then(() => pSub);
}, Promise.resolve())
.then(() => /* print all done */)
.catch( handleError );
jsfiddle with console.logs here
Any advice is highly appreciated. Being stuck at something so trivial is a bummer.
Your pSub
chains are all created and run synchronously during the reduce
call. To become sequential, they need to go inside the then
callback:
['job1', 'job2'].reduce((pMain, item, curIndex) => {
return pMain.then(() => {
var pSub = Promise.resolve();
for (let i = 0; i < 2; i++)
pSub = pSub.then(() => new Promise((resolve, reject) => setTimeout(reject, 2000)));
return pSub;
});
}, Promise.resolve())
Alternatively build only a single chain across the two loops:
['job1', 'job2'].reduce((promise, item, outerIndex) => {
return Array.from({length: 2}).reduce((promise, _, innerIndex) => {
return promise.then(() => new Promise((resolve, reject) => setTimeout(reject, 2000)));
}, promise);
}, Promise.resolve())
Of course @jfriend is right, for sequential tasks you should just write async
/await
code:
for (const item of ['job1', 'job2']) {
for (let i = 0; i < 2; i++) {
await new Promise((resolve, reject) => setTimeout(reject, 2000));
}
}
You can also easily put a try
block on the right level with that solution.