Converting JavaScript Callbacks to Promises
I use TypeScript for most anything nowadays. It's basically like programming in a less verbose C# or Java with a faster to prototype end result. One of the things that helps streamline code is the async/await functionality of TypeScript, which was folded into modern JavaScript. With async/await, instead of a callback:
function doStuff(key, callback) {
//my code
callback();
}
…which forces you to map out the flow of your application in a series of function calls within function calls, you can rely on JavaScript Promises.
A Promise is JavaScript's way of waiting for an asynchronous function to return. It is a built-in method with a then()
and a catch()
function that allows you to handle responses and exceptions:
function doStuff(key) {
return new Promise((resolve, reject) => {
try {
const MY_RETURN_STUFF = //my code
resolve(MY_RETURN_STUFF);
}
catch(e) {
reject(e);
}
});
}
This function returns a Promise. The Promise accepts an anonymous function that passing resolve()
and reject()
callbacks. When your code is complete, you call resolve()
, and pass it what you want to return. When your code fails, you pass the error message to reject()
.
You can call this function with:
doStuff(key)
.then((r) => {
//do something with the result
})
.catch((e) => {
//do something with the error
});
In this code, you could do the callback from the original code inside of the then()
.
This becomes even simpler with async/await:
try {
const result = await doStuff(key);
//do something with the result
}
catch(e) {
//do something with the error
}
You can see that with a function that returns a Promise, you can simply await
that function, and instead of using a then()
function, it just returns the result.
This is great, but sometimes there are libraries that are constructed in a event-driven way, where callbacks are necessary, but they don't return Promises, so you can't use async/await. This makes your code look clunky. How you can solve for it?
Luckily, there are a few packages that you can use to fix this by wrapping the object/events/functions in a Promise.
One of the most popular is probably es6-promisify:
npm install es6-promisify
Once you have the package installed, you can wrap non-Promise code in a Promise pretty easily:
import * as azure from "azure-storage";
import { promisify } from "es6-promisify";
const tableService = azure.createTableService(process.env.AZURE_STORAGE_ACCOUNT, process.env.AZURE_STORAGE_ACCESS_KEY);
const createTableIfNotExistsAsync = promisify(ats.createTableIfNotExists);
await createTableIfNotExistsAsync("testTable");
In the above code (written in TypeScript), we wrap the createTableIfNotExists()
method from the "azure-storage" library in a Promise, further cleaning up our code.