JS: Promise Tutorial
(new in ECMAScript 2015)
What is a promise
Purpose of promise
Promise is for async code dealing with operations that might take a long time.
The promise object
A Promise is an object that has three states:
- pending: initial state (neither fulfilled nor rejected)
- fulfilled: operation completed successfully
- rejected: operation failed
Once a promise state changed to fulfilled or rejected, it stays in that state (immutable).
- when the promise state is pending, we say it is unsettled.
- when the promise state is fulfilled or rejected, we say it is settled.
Outline of using promise
// creating an f_executor function. // the f_executor function is what you want to do, an operation that might take long time. const f_executor = (f_resolve, f_reject) => { // the names of the parameters f_resolve, f_reject doesn't matter. can be any name. // f_resolve, f_reject are functions generated by JavaScript internally. they are do-nothing dummy functions for the mechanism of promise. // do some operation here that might take long time. // call f_resolve(result_value) if the operation is successful. // the result_value is a value such as database query result. // the result_value is optional // call f_reject(message_or_error) if the operation failed. // message_or_error is any value or error object you want to indicate failure message. // message_or_error is optional }; // s------------------------------ // create a promise const xpromise = new Promise(f_executor); // s------------------------------ // deal with the promise // use .then(). 2-arg form. xpromise.then(f_yes, f_no); // if when promise is fulfilled, JavaScript calls f_yes(result_value). // result_value is the arg of f_resolve(result_value) earlier. // if when promise is rejected, JavaScript calls f_no(message_or_error). // message_or_error is the arg of f_reject(message_or_error) earlier. // or use then and catch. xpromise .then(f_yes) .catch(f_no) .finally(f_clean_up); // f_clean_up is always called.
Creating a promise
new Promise(f_executor)
// define the f_executor function // this is what you pass to new promise(f_executor). // the f_executor function is the heart of what you want to do. const f_executor = (f_resolve, f_reject) => { /* do something that might take long time. such as querying database. if success, you should call f_resolve, else call f_reject. when you call f_resolve, you can pass a value. e.g. f_resolve(op_result) op_result is optional. It may be the result of your database operation. (you can retrieve it in the promise object later.) when you call f_reject, you can also pass a value, usually a message or error object. e.g. f_reject("gone bad.") */ // here is our fake operation that takes long time. setTimeout(() => { const is_success = (Math.random() > 0.5) ? true : false; if (is_success) { f_resolve("value for resolved"); } else { f_reject("value for rejected."); } }, 999); }; // create a promise const xpromise = new Promise(f_executor); xpromise.then((yesss) => { console.log("Success:", yesss); }) .catch((oh_no) => { console.error("Error:", oh_no); }) .finally(() => { console.log("This part always runs."); });
Math gist of promise
here's its math essence.
const xpm = new Promise(ff);
xpm is a object with internal state of: pending, fulfilled, ejected. initial state is pending.
ff must be a function. And ff take 2 functions, aa, bb. Each is arity 1.
Parameters aa, bb are optional. (but almost always included in practice.)
return values of aa, bb, ff, are all ignored.
in ff, if it calls aa(xval), the state of xpm becomes fulfilled. (assuming xval itself is not a promise. if xval is itself a promise or a object withe a “then” method, the state of xpm is synced to the state of xval.)
xval is any value. It can be retrieved later in a xpm.then() method.
if it calls bb(xerr), state becomes rejected.
xerr is any value. It can be retrieved later in a xpm.then() method.
if it doesnt call aa nor bb, state is pending.
When
new Promise(ff)
is execeuted, it execute ff(faa,fbb), where faa fbb are dummy internal functions of JavaScript.
They are there as mechanism of promise.
They share a value for the state of a promise. As soon as one of them is called, it sets the promise state and cannot be changed. Later calls are ignored.
typically, the ff is named “executor function”, aa is called “resolve function”, bb is called “reject function”.
In ff, it should do whatever things you want to do, such as query database. what determines the state, is whether you call aa or bb.
What's the difference between p.then(yes_f, no_f) vs p.then(yes_f).catch(bad_f)
no_f (the second argument of .then) only sees a rejection of p itself.
If yes_f throws, or returns a rejected promise, no_f does not run. That rejection goes to whatever comes after this .then.
.catch sits after yes_f, so it sees:
- rejection of
p - a throw inside
yes_f - a rejected promise returned by
yes_f
What is thenable object?
an object with a then method.
a promise object is thenable.
This-binding in promise
this-binding in promise has value of undefined.
Tip. Rules to Remember
- A promise can only be resolved or rejected once
.then()always returns a new promise- Throwing inside
.then()rejects the promise