A Promise is an object representing the eventual completion or failure of an asynchronous operation.
Promises are a convenient way to organize the order of operation for your
program and provide and alternative to passing callbacks as function parameters.
Say we have a function callToDb
that makes a database call and returns a
promise
function success(result) {
// do something with result
}
function failed(error) {
// do something with error
}
callToDb('table_name').then(success, failed);
failed
is only called if an Error
is returned. Both of these arguments are
optional, however to use the result of the previous promise you need at least
a success function with one argument
callToDb('table_name')
.then(response => {
// do something with response
})
.catch(error => {
// do something with error
});
Like the above failed
function, catch
is only called if an Error
is
returned. then
returns a promise meaning we can now create a promise chain
.
callToDb('table_name')
.then(response => {
// do something with response
let res = response;
res.changesMade = true;
return res;
})
.then(response => {
// do more work
})
.catch(error => {
// do something with error
});
Chains can be as long as you need them. catch
can also be used multiple
times in a promise chain, the next catch
in the chain is called on return
of an Error
and following then
blocks will still be called.
callToDb('table_name')
.then(response => {
// do something with response
let res = response;
res.changesMade = true;
return res;
})
.then(response => {
// do more work
})
.catch(error => {
// only called for above thens
})
.then(response => {
// do more work
// will still happen after the catch, even if catch is called
})
.catch(error => {
// do something with error
// only called for the one above then if an Error is returned
});
The promise constructor should only be used to to wrap a function that does not
support a promise. Most libraries have built-in support for promises which
enable you to start chaining then
right out of the box without a promise
constructor.
The promise constructor takes one executor
function with two arguments:
resolve
and reject
. Let's create callToDb
, a wrapping function to a
function without promise support.
function callToDb(table_name) {
return new Promise((resolve, reject) => {
return db_orm(`select * from ${table_name}`, (err, res) => {
if(err) {
reject(err);
} else {
resolve(res);
}
})
});
}
A few things are happening here:
db_orm
is our database library without promise support, it takes a callback- wrapping
db_orm
is our returningPromise
which has our executor function withresolve
andreject
- once
db_orm
is in the callback we reject with the error, this will trigger acatch
or - we
resolve
with our result, this will trigger the nextthen
Reject returns a promise that is rejected with a reason
. To debug with ease
it is recommended to make the reason
an instance of Error
Promise.reject(new Error('My custom message'))
.then(result => {
// not called
})
.catch(result => {
console.log(result); // Error: My custom message
})
To reject a promise inside a then
chain you can return a new Error
or
throw an Error
to the catch.
Resolve returns a promise that is resolved with a result
. result
can also
be another promise
, thenable
or value.
Promise.resolve('Sweet!')
.then(result => {
console.log(res); // Sweet!
})
.catch(result => {
// not called
});