When dealing with asynchronous callbacks, it's important to note that they typically have an undefined ordering, although some exceptions like setTimeout
exist. The original calls themselves maintain order within their own execution context. While individual promise chaining can ensure the sequence of each callback, it's often advantageous to structure callbacks in a way where order is not critical and group asynchronous operations logically.
By arranging the callbacks to be order-agnostic, asynchronous operations can run simultaneously without a sequential serialization step introduced by chaining. Higher constructs such as "wait all" can also achieve parallel operation where the order of callbacks is independent.
For instance, instead of relying on k == files.length - 1
, employing a simple counter initialized to the files' length and decrementing it ensures the last callback remains the final one regardless of k's value. Additionally, inserting into blobs[k]
rather than pushing maintains the blob's order relative to the loop's iteration.
Utilizing Promise.all
, handling success through the finally block of a new promise amalgamates the resolved input promises' results without side effects to blobs. This emphasizes using the loop index for proper order management.
To further illustrate, if there is no dependency between two calls to saveArticles
, restructuring the code to prioritize stability over callback order ambiguity enables streamlined execution unaffected by deviations in callback sequencing.
let files: any[] = JSON.parse(element['files']);
let blobs = new Array(files.length);
let remaining = files.length;
for (let k = 0; k < files.length; k++) {
this.filesService
.getBinaryURI(files[k].url)
.then(blob => {
let blobElement = {
fileName: files[k].name,
fileType: files[k].type,
blob: blob,
};
blobs[k] = blobElement;
})
.finally(() => {
remaining -= 1;
if (remaining == 0) {
this.articleService.createArticle(element, this.artId, blobs);
}
});
}
An alternative approach involves leveraging Promise return values and chaining for enhanced flexibility, reduced complexity, and improved organization among saveArticle
calls without enforcing strict dependencies.
let files: any[] = JSON.parse(element['files']);
let reqs = [];
for (let k = 0; k < files.length; k++) {
var reqPromise = this.filesService
.getBinaryURI(files[k].url)
.then(blob => {
let blobElement = {
fileName: files[k].name,
fileType: files[k].type,
blob: blob,
};
return blobElement;
});
reqs.push(reqPromise);
}
let onAll = Promise.all(reqs).then((blobs) => {
return this.articleService.createArticle(element, this.artId, blobs);
});
The beauty of the latter method lies in the ability for saveArticles
calls to return the 'all promise', enabling superior composition control and ensuring correct synchronization when waiting for completion.
return onAll;
await saveArticles(articles, "edit");
await saveArticles(articles, "create");