A Promise is like a contract: it states that a value will be available in the future when a long-running operation has completed. In essence, a Promise represents the result of an asynchronous operation. When that value has been determined, the Promise executes the given code or handles any error associated with not having received the expected value.
Promises are first-class citizens of the JavaScript specification. They have three states:
When a Promise has been resolved, successfully or not, its value can’t change; it becomes immutable. We’ll discuss immutability in the section on functional programming later in this appendix.
To set up a Promise, you create a function that accepts two callback functions: one that executes on success and one that executes on failure. These callbacks fire when called by the Promise execution. Then, execution of the callbacks is transferred to then() functions (which are chainable) on success or a catch() function when not.
const promise = new Promise((resolve, reject) => { 1 // set up long running, possibly asynchronous operation, // like an API query if (/* successfully resolved */) { resolve({data response}); 2 } else { reject(); 3 } }); promise .then((data) => {/* execute this on success */}) 4 .then(() => {/ * chained next function, and so on */}) 5 .catch((err) => {/* handle error */}); 6
We use Promises in the Loc8r application, but not in a complicated way. The Promises API provides some static functions that help if you’re trying to execute multiple Promises.
Promise.all() accepts an iterable of Promises and returns a Promise when all items in the array fulfill or reject. The resolve() callback receives an array of responses: a mixture of Promise-like objects and other objects in order of fulfillment. If one of the executed Promises rejects, the reject() callback receives a single value.
const promise1 = new Promise((resolve, reject) => resolve() ); const promise2 = new Promise((resolve, reject) => resolve() ); const promise3 = new Promise((resolve, reject) => reject() ); const promise4 = new Promise((resolve, reject) => resolved() ); Promise.all([ promise1, promise2, promise3, promise4 ]) .then(([]) => {/* process success data iterable */}) 1 .catch(err => console.log(err)); 2
Promise.race() also accepts an iterable, but the output of Promise.race() is different. Promise.race() executes all provided Promises and returns the first response value that it receives whether this value is a fulfillment or a rejection.
const promise1 = new Promise((resolve, reject) => setTimeout(resolve, 1000, 'first') ); const promise2 = new Promise((resolve, reject) => setTimeout(reject, 200, 'second') ); Promise.race([promise1, promise2]) .then(value => console.log(value)) .catch(err => console.log(err)); 1
Because Promises rely on callbacks, due to their asynchronous nature, you can get into a muddle if several callbacks are nested. Finding yourself in a deeply nested callback structure is often referred to as callback hell. Promises somewhat mitigate this problem by providing a structure and making the asynchronicity explicit.
Promises have their drawbacks. They’re difficult to use in a synchronous manner, and you usually have to wade through a bunch of boilerplate code before getting to the good stuff.
async/await functions are there to simplify the behavior of using Promises synchronously. The await expression is valid only in an async function; if used outside an async function, the code throws a SyntaxError. When an async function is declared, the definition returns an AsyncFunction object. This object operates asynchronously via the JavaScript event loop and returns an implicit Promise as its result. The way the syntax is used and how it allows the code to be structured gives the impression that using async functions is much like using synchronous functions.
The await expression causes the execution of the async function to pause and wait until the passed Promise resolves. Then, function execution resumes.
One thing to point out is that await is not the same as Promise.then(). As await pauses the execution, causing code to execute synchronously, it isn’t chainable in the same way as Promise.then().
The next listing shows async/await in use.
function resolvePromiseAfter2s () { return new Promise(resolve => setTimeout(() => resolve('done in 2s'), 2000)); } const resolveAnonPromise1s = () => new Promise(resolve => setTimeout(() => resolve('done in 1s'), 1000)); async function asyncCall () { 1 const result1 = await resolvePromiseAfter2s(); 2 console.log(result1); 3 const result2 = await resolveAnonPromise1s(); 4 console.log(result2); 5 } asyncCall(); 6
You can find more details on async/await at https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function.
3.15.229.113