Async in JS
What does Asynchronous mean in Javascript
When you have a long-running synchronous code (e.g. generate 10,000,0000 prime numbers), JS code will be stuck until this code completes because JS is single threaded.
Async allows a long-running operation to return immediately with a Promise to be resolved in the future.
The thread will continue to the rest of the program, then resume when the long-running operation completes (resolves).
Promise
A Promise represents the future result of an asynchronous operation.
.then()
Handles resolved or rejected value of a Promise.
Takes in two parameters:
- callback functions for fulfilled Promiseandrejected Promise
function api() { return new Promise((resolve, reject) => { // ... }) }
Using .then() can write asynchronous code in a way that resembles synchronous code which is easier to follow.
const promise1 = new Promise((resolve, reject) => { resolve("Success!"); }); promise1.then((value) => { console.log(value); // "Success!" });
Chaining
.then stores the callbacks within the Promise it is called on and immediately returns another Promise object, allowing you to chain calls to other Promise methods.
api() .then(function(result){ return api2(); }) .then(function(result2){ return api3(); }) .then(function(result3){ // do something with result3 }) .catch(function(error) { //handle any error });
- catchis same as a- try { ... } catchblock structure.
If the return value within .then() is not a promise, it's implicitly wrapped in a Promise and then resolved.
const p2 = new Promise((resolve, reject) => { resolve(1); }); p2.then((value) => { console.log(value); // 1 return value + 1; }).then((value) => { // synchronous value works console.log(value); // 2 }); p2.then((value) => { console.log(value); // 1 });
Promise vs Callback
Promises are NOT Callbacks
Before Promise, Async JS codes relied on callback functions to be executed after a desired time.
- Although both Promiseandcallbackscan be used to achieve the async, they are fundamentally different things.
Chaining multiple asynchronous operations after other in a row with callback resulted in the pyramid of doom.
doSomething(function (result) { doSomethingElse(result, function (newResult) { doThirdThing(newResult, function (finalResult) { console.log(`Got the final result: ${finalResult}`); }, failureCallback); }, failureCallback); }, failureCallback);
With promise, you break out of the pyramid shape
const promise1 = doSomething(); const promise2 = promise1.then(successCallback, failureCallback);
async
Syntactical sugar for Promise.
// Arrow Function const foo = async(arr, callback) => { // use arr and callback } // Function Declaration async function foo() { // ... await ... } // anonymous async in callback const foo = event.onCall(async() => { // ... }) // in React async function fetchMovies() { const response = await fetch('/movies'); // waits until the request completes... console.log(response); } // example of useEffect in React useEffect(() => { const fetchData = async () => { // some code like fetch() }; await fetchData() .then( (res) => // ... ) .catch( (e) => console.error(e) );; }, [])
return in async
return
return in async function wraps the return value in Promise.resolve.
- wrapped Promiseof the value is returned.
Non-Promise return
Expression with non-promise value will be converted to a promise.
async function fn() { const a = await 9; } // becomes async function fn() { const a = await Promise.resolve(9); }
Missing return
async function foo() { try { canRejectOrReturn(); // no return } catch (e) { return 'caught'; } }
Async function without a return statement (like foo) will resolve with undefined.
Promise will STILL execute even without a return.
If your promise function has a side effect, the side effects will execute.
Async vs Promise
Main difference is the scope
Promise is kicked off right as they are defined, whereas async/await will execute one Promise at a time.
// Operation A & Operation B can run in parallel Promise.all([ returnsAPromise(opA), returnsAPromise(opB) ]) .then(res => { // waits info from both Operations A & B console.log("done") } ); // With Async // Operation A executes first, then Operation B const asyncFn = async () => { // Operation A runs first const resultA = await returnsAPromise(opA); // Operation B runs after Operation A completes const resultB = await returnsAPromise(opB); // Then, Operation C,D,E... runs console.log("done"); } asyncFn();
When using await, thread of execution will pause at the line containing await until the Promise is resolved.
- Even the synchronous code will wait until the awaitis complete.
async function example() { console.log("Step 1"); // synchronous await doSomethingAsync(); // pauses here until resolved console.log("Step 2"); // won't run until above promise resolves }
Synchronous vs Parallel Execution
When you have multiple awaits in a single function, an expression will run synchronously.
const test1 = async () => { const delay1 = await Promise.delay(600); //runs 1st const delay2 = await Promise.delay(600); //waits 600 for delay1 to run const delay3 = await Promise.delay(600); //waits 600 more for delay2 to run };
To parallel process multiple async/await, we use it with Promise.all() and combine scope via destructuring:
const asyncFnParallel = async () => { // Operation A & Operation B can run in parallel const [resultA, resultB] = await Promise.all([ returnsAPromise(opA), returnsAPromise(opB) ]); // Operation C,D,E... runs after Operation A and Operation B console.log("done"); } asyncFnParallel();
Execution Behavior of Promise vs Async
Promises run automatically without being called
You don't "declare" Promises:
- new Promisecreates a- Promise.- calls the executor function you pass it, synchronously, before new Promisereturns.
 
- calls the executor function you pass it, synchronously, before new 
If you want to define a Promise, but don't want to start it until specific point in time, you define it inside async function:
async function startPromise() { // ...other Promise can sit here too } let processPromise = startPromise();