Javascript code generally handles network & IO operations asynchronously: instead of blocking on a file read or database call, a callback is called when the data is ready.
There are three main ways of handling this in Javascript (and therefore Typescript) programs: callbacks, promises, and async/await.
Use async/await.
It looks like this:
async function readAndPrint(): Promise<void> {
const data = await readFilePromised("myfile")
console.log(data)
}
await readAndPrint()
If writing a script (rather than a server) you can probably use synchronous variants of methods, if they're available:
const data = fs.readFileSync("myfile");
console.log(data);
Callbacks look like:
readFileCallback("myfile", (data: string) => {
console.log("foo")
})
console.log("bob")
Even though foo
appears before bob
in the code, in almost all cases, bob
will be printed before foo
.
Callbacks lead to what's called callback hell:
readDirectory("mydir", (eachFile: string) => {
getFilePermissions(eachFile, (canReadFile: boolean) => {
if (canReadFile) {
readFileCallback(eachFile, (data: string) => {
console.log(data)
})
}
})
})
Promises came after callbacks. Instead of being passed a function to call once complete, promises return a variable that you can call .then
or .catch
on:
readFilePromised("myfile").then((data: string) => console.log(data))
Promises help with callback hell but can become very confusing when dealing with exceptions.
async/await are syntactic sugar that wraps promises and make asynchronous code easier to read and make exceptions more predictable to deal with.
There's a good introduction at the MDN: Making asynchronous programming easier with async and await
Any Javascript library that supports promises supports async/await.
If the library uses callbacks, you can use promisify
to wrap it in promises and then use it with async/await: util.promisify