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 Promise
andrejected 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 });
catch
is same as atry { ... } catch
block 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
Promise
andcallbacks
can 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
Promise
of 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
await
is 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
Promise
s run automatically without being called
You don't "declare" Promises
:
new Promise
creates aPromise
.- calls the executor function you pass it, synchronously, before new
Promise
returns.
- 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();