Promises
The built-in Promise
object provides a unified interface for managing asynchronous tasks.
There are many situations where a JavaScript program will have to pause for an unknown duration of time before it can continue working, like waiting on a response from a database or the result of running an external program. In the early days of JavaScript, programmers relied on callback functions to manage these kinds of asynchronous tasks.
JavaScript with callbacks is hard to get right intuitively. A lot of code ends up looking like this:
Callbacks also introduce a potential source of bugs and memory leaks with variable scoping, where a variable with the same name is defined multiple times in nested closures.
The use of Promise
objects, along with the built-in keywords async
and await
, allow programmers to order sequential instructions
for handling asynchronous tasks. It is common for TypeScript libraries and built-in Node.js modules to support a
Promise
-based approach to writing programs.
The Promise
object
A common way to create a Promise
is by constructing it with new Promise(...)
.
A Promise
object must be initialized with a function that takes two parameters, which are often labeled resolve
and reject
(although you could call them success
and failure
, done
and failed
, etc).
This function is called immediately when the Promise
is constructed. The arguments passed to resolve
and reject
are each functions which accept a single parameter.
resolve: (value: any) => any,
reject: (error: any) => any,
) => { ... })
If resolve
is called first, then the Promise
object uses the value
argument as the final result of the asynchronous
task it represents. If reject
is called first, then the Promise
object enters an "error state" where it will produce
the error instead.
then
and catch
Once the Promise
is created, you can use its then
and catch
methods to register callback functions that should
be executed with the result of resolve
and reject
respectively. Try modifying the simple example below:
The Promise
will only call one callback function exactly once - the function registered with then
, or the function registered with catch
.
Each of these function calls returns a new Promise
which represents the result of performing some further (possibly asynchronous) work.
If either function returns a value, that value will be passed to subsequent callback functions registered
with then
or catch
.
Resolve and reject
The Promise.resolve
and Promise.reject
functions create Promise
objects that immediately resolve or reject with the given values.
It's a useful shorthand for writing a full function to initialize a Promise
object and immediately call resolve
or reject
with a particular value.
The benefit of doing this, even for synchronous function that don't depend on asynchronous result, is that you can now use the function in a configurable chain of transformations that can include both synchronous and asynchronous work.
Async and await
The async
keyword indicates that a function returns a Promise
.
If the return type is not specified, TypeScript will attempt to infer the type of the returned Promise
. If the function is missing
type specifications, the return type will default to Promise<any>
, or Promise<void>
if the function does not return anything.
One of the key advantages to using Promise
s is the ability to use async
and await
.
async
functions
An async function must return a correctly-typed Promise
object.