How to wrap a TypeScript JS callback with async await

Thu, 25 Feb 2021 00:00 UTC by garethbrown

I find that at times the standard TypeScript/JavaScript callback pattern can make code hard to read and manage. To alleviate this problem, we can wrap callbacks with promises to synchronise the call. Going a step further, we can implement async / await to further improve code readability. This is especially true if like me, your primary programming language is OO like C# or Java.

In the below example, I’m working with NeDB, as JS database. Operations in NeDB follow the standard callback pattern. Standard callback pattern in the docs looks like this:

db.findOne({'name': 'draupnir'}, function (err, doc) {  
    console.log('findOne callback: err, doc', [err, doc]);  
});

In the async / await version below, we create an async function that wraps a promise, allowing us to call await to synchronise on the result. To allow for error handling in case promise reject() is called, we wrap the await in a try-catch-finally block.

In the standard promise version, we simply use findOnePromise.then() to inspect the result.

So, depending on your preference, you have some options …

///////////////////////
// async / await vesion
///////////////////////

let findOnePromiseFn = async (obj):Promise<any> => {

    return new Promise((resolve, reject) => {

        db.findOne(obj, function (err, doc) {

            if(err)
            {
                reject(err);
            }
            else
            {
                resolve(doc);
            }
        });
    })
}

// Trigger the function

let findOnePromise = findOnePromiseFn({'name': 'draupnir'});

// Potentially do other stuff ...

// Await the result

try
{
    let findOnePromiseResult = await findOnePromise;

    console.log('findOnePromiseResult', findOnePromiseResult);
}
catch (e)
{
    console.log('findOnePromiseResult (Error)', e);
}
finally
{
    // Any clean up
}
  
/////////////////////////////////
// standard promise based version
/////////////////////////////////
  
let findOnePromisFn = () => {

    return new Promise((resolve, reject) => {

        db.findOne({'name': 'draupnir'}, function (err, doc) {

            if(err)
            {
                reject(err)
            }
            else
            {
                resolve(doc);
            }
        });
    })
}

findOnePromisFn().then(
    value => {

        // Success

        console.log('findOnePromiseResult', value);
    }, 
    reason => {

        // Error

        console.error('findOnePromiseResult (Error)', reason);
    }
);

//////////////////////////////////
// Standard async callback version
//////////////////////////////////

db.findOne({'name': 'draupnir'}, function (err, doc) {

    console.log('findOne callback: err, doc', [err, doc]);
});
UI block loader
One moment please ...