This article walks you through all things about asynchronous JavaScript including why asynchronous is needed, how it works for a single-threaded JavaScript and how to do asynchronous programing. A lot of content is from MDN but reorganized for a better understanding.
JavaScript is an inherently single-threaded language. By default (unless you uses a web worker which is provided by the browser, not part of the JavaScript language) , the browser uses a single thread (the main thread) to run all the JavaScript in your page and to perform other stuff like event receiving/dispatching, UI updating. But for long-running operations, making them asynchronous is needed.
All content talked here if not specified, is about JavaScript in a browser environment.
Why asynchronous programming is needed?
A basic problem with a long-running synchronous function is that it will block the thread and make our program unresponsive. To avoid the function to block the thread we can make the long-running function being asynchronous like:
- When it is called it returns immediately and our program can still be responsive.
-
When it eventually completes our program can be notified with its result.
Many functions provided by browsers take a long time, therefore they are asynchronous. Some of them are (see more ):
fetch()
fetches a resource from the network.-
showOpenFilePicker()
shows a file picker to ask the user to select files.
Note
Such Browser APIs are built into your web browser and are not part of the JavaScript language itself. They are built on top of the core JavaScript language and executed by the browser itself, not by the JavaScript engine which executes your JavaScript code.
See more in Introduction to web APIs – Learn web development | MDN.
How asynchronous functions are implemented?
About how asynchronous function is implemented, you might have thought of the way an event is handled. You are right, in fact some early asynchronous APIs used events in just this way.
JavaScript has a runtime model based on an event loop. The event loop processes tasks one after another. It is something like:
// queue.waitForMessage() waits synchronously for a message to arrive
// (if one is not already available and waiting to be handled).
while (queue.waitForMessage()) {
queue.processNextMessage();
}
The event loop
JavaScript has a runtime model based on an event loop, which is responsible for executing the code, collecting and processing events, and executing queued sub-tasks.
When an event occurs, its handler(callback) will be added into the task queue and run at an appropriate time. This is really a form of asynchronous programming.
Some early asynchronous APIs used events in just this way. Like you call XMLHttpRequest
to make a HTTP request to a remote server and you need to define a listener for the loadend
event to get notified about the eventual completion of the request.
const xhr = new XMLHttpRequest();
xhr.addEventListener('loadend', () => {
console.log(`Finished with status: ${xhr.status}`);
});
xhr.open('GET', 'https://example.com/example.json');
xhr.send();
The asynchronous function returns immediately and the request will be going on in the browser. At the same time our JavaScript code which is executed by the JavaScript engine continues to run. And our event handler will be called when the request is complete.
Task queue
Here it just mentions one queue for simplicity. In fact, there can be more than one queues so that events/messages can have different priorities and they are put into different queues.
Promise
Now you have known the way how asynchronous functions were implemented in JavaScript. However, sometimes callback-based code will become not elegant . Like we need preform an operation that needs a series of asynchronous functions, we nest callbacks like:
function doOperation() {
doStep1(0, (result1) => {
doStep2(result1, (result2) => {
doStep3(result2, (result3) => {
console.log(`result: ${result3}`);
});
});
});
}
doOperation();
This is called “callback hell” and it needs to handle errors at each level.
For these reasons, most modern asynchronous APIs don’t use callbacks but Promise
. But the mechanism behind is similar.
Promise
Basic usage
You can think a promise as an object returned by an asynchronous function. When the promise is returned, the operation often isn’t finished. But it allows you to attach handlers to this promise object. When the operation is finished, the promise will call our handler asynchronously, passing the final result or an error of the operation as the parameter.
Promise description
A
Promise
is a proxy for a value not necessarily known when the promise is created. This lets asynchronous methods return values like synchronous methods, merely it returns a Promise and supply the final value in the future.
Below is an example to make a HTTP request to a remote server with the fetch()
API that is based on promise.
const fetchPromise = fetch('https:example.com/p1.json');
// Here the fetchPromise's state is "pending"
console.log(fetchPromise); // Output: Promise {}
// When the fetch operation succeeds, fetchPromise gets resolved with
// a Response object returned by fetch as its value
// and fetchPromise calls our handler, passing its value as the parameter.
fetchPromise.then((response) => {
// Output: Received response: 200
console.log(`Received response: ${response.status}`);
console.log(fetchPromise); // Output: Promise {: Response}
});
The then()
takes up to two parameters: onFulfilled
and onRejected
. They are callback functions (handlers) for the success and failure cases of the Promise
. Both of them are optional.
Note:
If
onFulfilled
is not a function, it is internally replaced with an identity function ((x) => x
) which simply passes the fulfillment value forward.If
onRejected
. is not a function, it is internally replaced with a thrower function ((x) => { throw x; }
) which throws the rejection reason it received.
It returns a newly generated Promise
. When a handler(onFulfilled
/onRejected
) is called, the promise gets resolved with the value returned by the handler as its value (if the handler does return a value and it is not a promise).
Basically if the handler is called and:
- doesn’t return anything, the promise get resolved with
undefined
as its value.returns another promise (let’s call it B), the promise (let’s call it A) returned by
then()
follows it.A follows B means if B gets resolved, A gets resolved, if B never gets resolved, A won’t either.
See more about the rules that the behavior of the handler follows at Promise.prototype.then() – JavaScript | MDN.
Chain promises
As you saw above, then()
returns a promise so that it can be used for chaining.
Below is an examples which shows how to chain multiple promises to preform an operation that needs a series of asynchronous functions as mentioned in the previous section.
doAsyncStep1(0)
.then((result1) => doAsyncStep2(result1))
.then((result2) => doAsyncStep3(result2))
.then((result3) => {
console.log(`result: ${result3}`);
});
In this example, each subsequent asynchronous function starts when the previous one succeeds, with the parameter comes from the previous step.
Always return result in chain
In the above example, each
onFulfilled
handler is an arrow function with only an expression which is the implicit return value.In other cases, if the arrow function has a block body or a normal function, you need an explicit
return
statement, otherwise the subsequent handlers won’t get the result of the previous one. And a worse issue is that the next handler will be called earlier because the previous one returns nothing and make the corresponding promise gets resolved withundefined
as its value.See at Using Promises.
Below is an example that gets the response data after a request is completed. See at How to use promises – chaining promises
const fetchPromise = fetch('http://example.com/p1.json');
fetchPromise
.then((response) => response.json()) // Here it does not handle
// the situation that respone.ok is not true
.then((data) => {
console.log(data[0].name);
});
The json()
method of the Response
object is also asynchronous and returns a promise.
More about the then()
Below is another example that shows the process of the then()
is executed.
function foo() {
const p = new Promise((resolve, reject) => {
resolve('Success');
});
console.log('p', p); // p: Promise {:
return p.then((value) => {
console.log(value); // 'Success'
return 'Success2';
}, (e) => {
console.log(e.message);
});
}
const result = foo();
console.log('Result:', result); // Result: Promise {}
// By using setTimeout, we postpone the execution of `console.log(result)`
setTimeout(() => console.log(result), 0);
// Output:
// p: Promise {: 'Success'}
// Result: Promise {}
// Success
// Promise {: 'Success2'}
Here is how:
- When you call
foo()
, it first create a resolved promisep
. It is printed in the console:p: Promise {: 'Success'}
. -
After that the
then()
is called on the promisep
.then()
creates and returns a pending promise which is then returned byfoo()
.Note the handlers added to
then()
are always called asynchronously. Even though herep
is alreadyfulfilled
, the handler ofp.then()
will not be called immediately but is put on a microtask queue. See more at Using Promises: Timing. -
The pending promise is printed in the console:
Result: Promise {}
. -
setTimeout
postpone the execution ofconsole.log(result)
. -
At some point,
onFulfilled
handler ofthen()
is called, andSuccess
is printed in the console. The promise returned bythen()
gets resolved withSuccess
as its value. -
At some point,
console.log(result)
is executed and the promise which has get resolved is printed in the console:{: 'Success'}
In the example, if p
is a rejected promise that is:
const p = new Promise((resolve, reject) => {
// or throw new Error(’fail');
reject(new Error('Fail'));
});
The output would be:
// p: Promise {<rejected>: Error: Fail
// Result: Promise {<pending>}
// Fail
// Promise {<fulfilled>: undefined}
Attaching handlers for a promise with then()
is like defining a handler for an event. The handler in then()
is called asynchronously at some point in the event loop.
Using Promises – Guarantees
Unlike old-fashioned passed-in callbacks, a promise comes with some guarantees:
- Callbacks added with
then()
will never be invoked before the completion of the current run of the JavaScript event loop.- These callbacks will be invoked even if they were added after the success or failure of the asynchronous operation that the promise represents.
- Multiple callbacks may be added by calling
then()
several times. They will be invoked one after another, in the order in which they were inserted.
See more at Promise.prototype.then() and Let’s talk about how to talk about promises.
Error handling with catch()
The Promise
object provide a catch()
method to handle errors. Like then()
, the handler passed to catch()
is called when the asynchronous operation fails.
An example with error handling.
const fetchPromise = fetch('https:example.com/p1.json');
fetchPromise
.then((response) => {
if (!response.ok) {
throw new Error(`HTTP error: ${response.status}`);
}
return response.json();
})
.then((data) => {
console.log(data[0].name);
})
.catch((error) => {
console.error(`Could not get products: ${error}`);
});
finally():
The
finally()
method can be useful if you want to do some processing or cleanup once the promise is settled, regardless of its outcome.The main differences between
finally(finally)
andthen(onFinally, onFinally)
it that the return value of the handler offinally()
does not affect the state of the original promise, unless the handler throws an error or returns a rejected promise.// The returned promise will gets resolved with 2 console.log( Promise.resolve(2).finally(() => 77) ); // The returned promise will gets resolved with 2 console.log( Promise.resolve(2).finally(() => Promise.resolve(77)) ); // The returned promise will gets rejected with 99 console.log( Promise.reject(3).finally(() => Promise.reject(9 9)) ); // The returned promise will gets rejected with 99 console.log( Promise.reject(3).finally(() => {throw 99}) );
About return value
catch()
also returns a Promise
and it can be used for chaining too. But unlike then()
, it has one handler and the handler may have no chance to be called. This may remind you a question. Like the example showed in the previous section, if a function returns a promise with catch()
attached then how the returned promise gets resolved.
Promise.prototype.catch() tells that catch()
internally calls then()
and gives an example to observe that. More clearly, catch()
internally calls then()
with parameters undefined
and its own onRejected
handler. And it returns the value that then()
returns.
In fact, catch()
acts just like a then(undefined, onRejected)
. Hence it is clear what happens in the example below.
An example in which a catch()
is chained to a promise.
function foo() {
const p = new Promise((resolve, reject) => {
resolve('Success');
});
console.log('p:', p); // p: Promise {: 'Success'}
return p.catch((e) => {
console.error('Fired in catch:', e.message); // Never called.
});
}
const result = foo();
console.log('Result:', result); // Result: Promise {}
// By using setTimeout, we postpone the execution of `console.log(result)`
setTimeout(() => console.log(result), 0); // Promise {: 'Success'}
// Output:
// p: Promise {: 'Success'}
// Result: Promise {}
// Promise {: 'Success'}
A
.catch()
is really just a.then()
without a slot for a callback function for the case when the promise is fulfilled.
A rejected callback in then()
or a catch()
If there is no need to handle errors immediately, you can just leave out an error in a promise chain to fall back to the final catch()
. Let’s rewrite the example by adding error handling:
doAsyncStep1(0)
.then((result1) => doAsyncStep2(result1))
.then((result2) => doAsyncStep3(result2))
.then((result3) => {
console.log(`result: ${result3}`);
})
.catch((error) => {
// The error callback will be called when any of the
// asynchronous doAsyncStepX calls fails.
console.error(error);
});
If you need to handle an error immediately, just provide a rejection callback to a proper then()
. Note if that rejection callback is called, you need to throw an error to make the error down the chain, otherwise the promise returned by that then()
will get fulfilled
.
Gotchas when throwing errors:
Errors thrown inside asynchronous functions will act like uncaught errors:
const p2 = new Promise((resolve, reject) => { setTimeout(() => { throw new Error("Uncaught Exception!"); }, 1000); }); p2.catch((e) => { console.error(e); // This is never called });
Another eample:
function foo(request) { // caches.match() gets the reponse for a request in the caches. return caches.match(request) .then(undefined, error => { // Error in the asynchronous open() won't be caught // in the final catch. caches.open(); // Its error will be caught in the final catch, // because the promise A returned by then() follows // the promise B returned by match(). When B gets // gets rejected, A gets rejected either and // the handler in the final catch will be called. return catches.match('/default.jpg'); }) .catch(function (error) { console.log('final match error:', error); }); }
Errors thrown after resolve is called will be silenced:
const p3 = new Promise((resolve, reject) => { resolve(); hrow new Error("Silenced Exception!"); }); p3.catch((e) => { console.error(e); // This is never called });
Promise.all()
and Promise.any()
If you need to be notified when all the promises to be fulfilled which don’t depend on each other, Promise.all()
is what you want.
Promise.all()
is a static method which takes an array of promises and returns a single promise. The returned promise is fulfilled when and if all the promises in the array are fulfilled, and it is rejected when and if any of the promises is rejected.
Example:
const fetchPromise1 = fetch('https://example.com/p1.json');
const fetchPromise2 = fetch('https://example.com/p2.json');
const fetchPromise3 = fetch('https://example.com/p3.json');
Promise.all([fetchPromise1, fetchPromise2, fetchPromise3])
.then((responses) => {
for (const response of responses) {
console.log(`${response.url}: ${response.status}`);
}
})
.catch((error) => {
console.error(`Failed to fetch: ${error}`)
});
If you need any one of a set of promises to be fulfilled not all, you will want Promise.any()
. Its usage is similar and not listed hare any more.
States of a promise
A promise can be in one of three states:
- pending: initial state, the asynchronous function has not completed.
- fulfilled: the asynchronous function has succeeded, the first handler(
onFulfillment
parameter) passed tothen()
is called. - rejected: the asynchronous function has failed, its
catch()
handler is called.
Terms: settled, resolved
A promise is settled if it is fulfilled or rejected.
A promise is resolved if it is settled or it has been “locked in” to follow the state of another promise (by attaching
then()
to it).See more in Let’s talk about how to talk about promises.
The eventual state of a pending promise can either be fulfilled with a value or rejected with a reason (error).
See more at Promise and How to use promises.
Promise constructor
You have seen how to create a Promise in the examples mentioned above. It accepts a function called executor which will be executed by the constructor. The function executor accepts will receive two functions resolutionFunc
and rejectionFunc
as parameters.
The constructor returns a promise which will become resolved when either of the functions resolutionFunc
or rejectionFunc
are invoked.
Note that when you call resolutionFun(value)
or rejectionFunc(value)
, the value can also be another promise, in which case the promise gets dynamically inserted into the promise chain (In such case, the constructed promise is resolved but still not settled).
Below example wraps a callback-style asynchronous to support promise.
function myAsyncFunction(url) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open("GET", url);
xhr.onload = () => resolve(xhr.responseText);
xhr.onerror = () => reject(xhr.statusText);
xhr.send();
});
}
See more at MDN: Promise() constructor.
Browser support of promises
JavaScript Promises: an introduction:
Browser support & polyfill
There are already implementations of promises in browsers today.
As of Chrome 32, Opera 19, Firefox 29, Safari 8 & Microsoft Edge, promises are enabled by default.
To bring browsers that lack a complete promises implementation up to spec compliance, or add promises to other browsers and Node.js, check out the polyfill (2k gzipped).
Async and await
The async
and await
keywords provides a cleaner way to work with promise-based code, avoiding explicitly configuring promise chains.
Adding async
at the start of a function makes it an async
function. Inside an async
function, you can put await
before a call to a function that returns a promise.
Let’s see an example to find out the effect of a await expression.
async function foo() {
console.log('foo'); // It will be executed synchronously.
// An await expression will make the progress suspended.
const result1 = await new Promise((resolve) =>
setTimeout(() => resolve('1'))
);
console.log('result1 =', result1); // Called asynchoronously
const result2 = await new Promise((resolve) =>
setTimeout(() => resolve('2'))
);
console.log('result2 =', result2);
}
console.log('before foo');
foo(); // Here foo() returns a promise with pending state.
console.log('after foo');
// Output:
// before foo
// foo
// after foo
// result1 = 1
// result2 = 2
Code after each await expression (including the assignment of the await expression) acts like the code inside a handler of a then()
.
The effect is equivalent to:
function foo() {
console.log('foo');
new Promise((resolve) =>
setTimeout(() => resolve('1'))
).then((result1) => {
console.log('result1 =', result1);
}).then(() => {
return new Promise((resolve) =>
setTimeout(() => resolve('2'))
);
}).then((result2) => {
console.log('result2 =', result2);
})
}
await
Note that you can only use
await
inside anasync
function, unless your code is in a JavaScript module.
Return value of an async
function
An async function always return a promise.
If the async function returns:
- returns a value that is not a promise, the promise will gets resolved with that value.
-
throws an error, or uncaught within, the promise gets rejected with the error.
-
returns nothing, the promise will not gets resolved or rejected, i.e. remains pending.
Error handling
async function foo() {
console.log('foo');
const result1 = await new Promise((resolve) =>
setTimeout(() => resolve('1'))
);
console.log('result1 =', result1);
const result2 = await new Promise((resolve, reject) => {
setTimeout(() => reject('fail'));
});
console.log('result2 =', result2);
}
console.log('before foo');
foo()
.catch((e) => console.log('catch:', e));
console.log('after foo');
// Output:
// before foo
// foo
// after foo
// result1 = 1
// catch: fail
If there is no catch
in the example:
console.log('before foo');
var fooResult = foo();
console.log('after foo');
setTimeout(() => console.log('after foo, fooResult =', fooResult), 1000);
// Output:
// before foo
// foo
// after foo
// result1 = 1
// Uncaught (in promise) fail
// after foo, fooResult = Promise {<rejected>: 'fail'}
An parallel example
async function concurrentStart() {
console.log("==CONCURRENT START with await==");
const slow = resolveAfter2Seconds(); // starts timer immediately
const fast = resolveAfter1Second(); // starts timer immediately
// 1. Execution gets here almost instantly
console.log(await slow); // 2. this runs 2 seconds after 1.
console.log(await fast); // 3. this runs 2 seconds after 1., immediately after 2., since fast is already resolved
}
Async
function vs a function returns a Promise
Async function vs a function that returns a Promise (2018):
Async function vs a function returns a Promise
There is a small, but quite important difference between a function that just returns a Promise, and a function that was declared with the
async
keyword.Take a look at the following snippet:
function fn(obj) { const someProp = obj.someProp return Promise.resolve(someProp) } async function asyncFn(obj) { const someProp = obj.someProp return Promise.resolve(someProp) } asyncFn().catch(err => console.error('Catched')) // => 'Catched' fn().catch(err => console.error('Catched')) // => TypeError: Cannot read proper
JavaScript will make sure that the
asnycFn
will return with a Promise (either resolved or rejected) even if an error occurred in it, in our case calling our .catch() block.However with the
fn
function the engine doesn’t yet know that the function will return a Promise and thus it will not call our catch() block.
Workers
A worker enables you to run some tasks in a separate thread. Workers are Web APIs that are provided by the browser and not part of the JavaScript language.
A challenge with multithreaded code is that two threads may access the same variables and causes an inconsistence issue.
Introducing workers: To avoid this on the web, your main code and your worker code never get direct access to each other’s variables. Workers and the main code run in completely separate worlds, and only interact by sending each other messages. In particular, this means that workers can’t access the DOM (the window, document, page elements, and so on).
There are three different sorts of workers:
- Dedicated workers
- Shared workers can be shared by several different scripts running in different windows.
- Service workers act like proxy servers, caching resources so that web applications can work when the user is offline. They’re a key component of Progressive Web Apps.
The last two types of workers are not introduced here.
Web worker
An simplified example from Introducing workers that use a worker to generates prims.
generate.js
:
// Listen for messages from the main thread.
// If the message command is "generate", call `generatePrimes()`
addEventListener('message', (message) => {
if (message.data.command === 'generate') {
generatePrimes(message.data.quota);
}
});
// A synchronous function which generates primes (very inefficiently)
// of a count specified by quoto.
function generatePrimes(quota) {
// Generate primes and put them into an array primes
// const primes = [];
// ...
// When we have finished, send a message to the main thread,
// including the number of primes we generated.
postMessage(primes.length);
}
In the above code, we start listening a message from the main script. If a message is sent, in the handler we reads the data
property which is a copy from the main script and call generatePrimes
. When the prims are done, we use postMessage() – Web APIs and pass it the count of prims generated to send a message to the main script. Similar, the data we passes to postMessage
is a copy from the worker to the main script.
main.js
:
// Create a new worker, giving it the code in "generate.js"
const worker = new Worker('./generate.js');
worker.postMessage({
command: 'generate',
100,
});
// When the worker sends a message back to the main thread,
// print the number of primes that were generated,
// taken from the message data.
worker.addEventListener('message', (message) => {
console.log(`Finished generating ${message.data} primes!`);
});
Reference
JavaScript Runtime and the event loop
- MDN: The event loop
> ### Stack
>
> Function calls form a stack of frames.
>
> Note that the arguments and local variables may continue to exist, as they are stored outside the stack — so they can be accessed by any nested functions long after their outer function has returned.
>
> ### [“Run-to-completion”](The event loop – JavaScript | MDN “Permalink to “Run-to-completion””)
>
> Each message is processed completely before any other message is processed.
>
> This offers some nice properties when reasoning about your program, including the fact that whenever a function runs, it cannot be preempted and will run entirely before any other code runs (and can modify data the function manipulates).
>
> ### Several runtimes communicating together
>
> A web worker or a cross-originiframe
has its own stack, heap, and message queue. Two distinct runtimes can only communicate through sending messages via thepostMessage
method. This method adds a message to the other runtime if the latter listens tomessage
events.
>
> ### Never blocking
>
> A very interesting property of the event loop model is that JavaScript, unlike a lot of other languages, never blocks. Handling I/O is typically performed via events and callbacks, so when the application is waiting for an IndexedDB query to return or an XHR request to return, it can still process other things like user input. -
> Introducing asynchronous JavaScript
>
> In this article, we’ll learn about synchronous and asynchronous programming, why we often need to use asynchronous techniques, and the problems related to the way asynchronous functions have historically been implemented in JavaScript.
>
> How to use promises
>
> Here we’ll introduce promises and show how to use promise-based APIs. We’ll also introduce theasync
andawait
keywords.
>
> Implementing a promise-based API
>
> This article will outline how to implement your own promise-based API.
>
> Introducing workers
>
> Workers enable you to run certain tasks in a separate thread to keep your main code responsive. In this article, we’ll rewrite a long-running synchronous function to use a worker. -
MDN: In depth: Microtasks and the JavaScript runtime environment
> Starting with the addition of timeouts and intervals as part of the Web API (
setTimeout()
andsetInterval()
), the JavaScript environment provided by Web browsers has gradually advanced to include powerful features that enable scheduling of tasks, multi-threaded application development, and so forth. To understand wherequeueMicrotask()
comes into play here, it’s helpful to understand how the JavaScript runtime operates when scheduling and running code.
>
> ### JavaScript execution contexts
>
> When a fragment of JavaScript code runs, it runs inside an execution context.
>
> There are three types of code that create a new execution context:
>
> – The global context is the execution context created to run the main body of your code; that is, any code that exists outside of a JavaScript function.
> – Each function is run within its own execution context. This is frequently referred to as a “local context.”
> – Using the ill-advisedeval()
function also creates a new execution context.
>
> As one code segment begins execution, a new context is constructed and is placed on the execution context stack. When it exits, the context is removed from the context stack.
>
> Using execution contexts in this manner, each program and function is able to have its own set of variables and other objects.
>
> ### Event loops
>
> Each agent is driven by an event loop, which collects any user and other events, enqueuing tasks to handle each callback. It then runs any pending JavaScript tasks, then any pending microtasks, then performs any needed rendering and painting before looping again to check for pending tasks.
>
> ### Task queue vs microtask queue
>
> The difference between the task queue and the microtask queue is simple but very important:
>
> – When executing tasks from the task queue, the runtime executes each task that is in the queue at the moment a new iteration of the event loop begins. Tasks added to the queue after the iteration begins will not run until the next iteration.
> – Each time a task exits, and the execution context stack is empty, each microtask in the microtask queue is executed, one after another. The difference is that execution of microtasks continues until the queue is empty—even if new ones are scheduled in the interim. In other words, microtasks can enqueue new microtasks and those new microtasks will execute before the next task begins to run, and before the end of the current event loop iteration. -
The Node.js Event Loop, Timers, and process.nextTick()
> ## What is the Event Loop?
>
> The event loop is what allows Node.js to perform non-blocking I/O operations — despite the fact that JavaScript is single-threaded — by offloading operations to the system kernel whenever possible.
>
> ## Event Loop Explained
>
> When Node.js starts, it initializes the event loop, processes the provided input script (or drops into the REPL, which is not covered in this document) which may make async API calls, schedule timers, or callprocess.nextTick()
, then begins processing the event loop.
>
> The following diagram shows a simplified overview of the event loop’s order of operations.
>
>sql
> ┌───────────────────────────┐
> ┌─>│ timers │
> │ └─────────────┬─────────────┘
> │ ┌─────────────┴─────────────┐
> │ │ pending callbacks │
> │ └─────────────┬─────────────┘
> │ ┌─────────────┴─────────────┐
> │ │ idle, prepare │
> │ └─────────────┬─────────────┘ ┌───────────────┐
> │ ┌─────────────┴─────────────┐ │ incoming: │
> │ │ poll │ │ └─────────────┬─────────────┘ │ data, etc. │
> │ ┌─────────────┴─────────────┐ └───────────────┘
> │ │ check │
> │ └─────────────┬─────────────┘
> │ ┌─────────────┴─────────────┐
> └──┤ close callbacks │
> └───────────────────────────┘
>
Workers
Web API
-
How does the Event Loop works in JavaScript? 2021.03
Much its content is like a text version of a video: Philip Roberts: Help, I’m stuck in an event-loop. 2014.05.25. The author of the video Philip Roberts created a website Loupe to show how the event loop works for a given piece of code.
> ### The Web API
>
> This is where code that isn’t handled by the V8 engine is executed to not “block” the main execution thread. When the Call Stack encounters a web API function, the process is immediately handed over to the Web API, where it is being executed and freeing the Call Stack to perform other operations during its execution. -
> ### APIs in client-side JavaScript
>
> Client-side JavaScript, in particular, has many APIs available to it — these are not part of the JavaScript language itself, rather they are built on top of the core JavaScript language, providing you with extra superpowers to use in your JavaScript code. They generally fall into two categories:
>
> – Browser APIs are built into your web browser and are able to expose data from the browser and surrounding computer environment and do useful complex things with it.
>
> – Third-party APIs are not built into the browser by default. For example, the Twitter API allows you to do things like displaying your latest tweets on your website. -
Web APIs a list of all the APIs and interfaces (object types).