There are many articles on converting javascript callbacks to Promises. Like this one, for instance.
So why one more? Ok. Maybe this is better! Or, at the very least, it gives you one more approach to choose from.
Most of the solutions available have you coding the callback inside the promise. The method described here takes the callback function and returns both a modified callback, along with a linked promise. This promise resolves when that modified callback is executed. This helps when a callback has to be passed to an external module. Instead of the original callback, we pass the modified callback. The linked promise can then be awaited and will resolve when the modified callback is called.
In the majority of cases, it is sufficient to always resolve a promise with a result. The value of the result can be overloaded for error or success. The promise then always resolves successfully and the error condition is indicated by the value of the result. Oftentimes this is much easier than rejecting a promise and handling the reject exception in the await. Assuming this to be the case, the code to implement the conversion of callbacks to promises becomes as simple as the function below:
//======================================
function getLinkedPromiseAndMcb(cb) {
//get the promise to await and the modified cb that will
//fulfill the promise
//Glossary: r=resolve fcn, lp=linked promise, Mcb=modified callback
let r=null //declared here so it non-local to functions in this scope
let lp=new Promise((resolve,reject) =>
{r=resolve})
let Mcb =(...args)=>
{r(cb(...args))}
return [lp,Mcb]
}//========================================
This function getLinkedPromiseAndMcb
takes a callback function as a parameter and returns a modified callback function (Mcb) along with a promise linked to that Mcb (called lp in the code).
Here’s a visual representation:
The Mcb can be used as the callback function to be passed to some external module. The linked promise (lp) will resolve when the Mcb is called ‘back’ from some external module.
The usage of getLinkedPromiseAndMcb
is as follows:
//==========================================
//cb is the original callback function
let [lp,mcb] = getPromiseAndMcb(cb)
//start the time-consuming ‘external’ code that eventually calls the mcb
let rslt = await lp; //the lp promise resolves with value returned by the callback (cb)
//==========================================
Here is a complete sample use case that can be executed in NodeJS (The current NodeJS includes the async module):
====nodejs example
function getLinkedPromiseAndMcb(cb) {
//get the promise to await and the modified cb that will
//fulfill the promise
//Glossary: r=resolve fcn, lp=linked promise, Mcb=modified callback
let r=null //declared here so it non-local to functions in this scope
let lp=new Promise((resolve,reject) => {r=resolve})
let Mcb =(...args)=> {r(cb(...args))}return [lp,Mcb]}
//sample testing function
async function tester() {
let cb=(rslt)=>{console.log(`Result = "${rslt}" received`)} //sample callback
let [lp,Mcb] = getLinkedPromiseAndMcb(cb)
//simulate an external process returning after 5000 ms
let rt=5000
setTimeout(()=>Mcb('callback Rslt'),rt) //simulates external process calling back cb with a result
console.log("Awaiting callback")
await lp
console.log('done')}
tester()
//save the above example in a file (‘x.js’)
============ Testing the sample===========
node x.js
//output shown in bold below
Awaiting callback
//after a wait of 5 seconds, callback returns
Result = "callback Rslt" received
done