JavaScript Visualized - Promise Execution

Lydia Hallie
24 Mar 202408:42

Summary

TLDRThis script demystifies JavaScript Promises by explaining their creation, execution, and handling behind the scenes. It clarifies how Promises work with async tasks, the event loop, and microtask queue, using a practical example with setTimeout. The explanation simplifies Promise chaining and the non-blocking nature of asynchronous JavaScript, encouraging viewers to understand and utilize Promises effectively in their coding.

Takeaways

  • 😎 Promises in JavaScript can seem intimidating but are not as complicated once you understand their mechanics.
  • 🛠 A Promise is created using the `new Promise` constructor, which takes an Executor function and creates a Promise object with internal slots for state, result, and reactions.
  • 🔄 The Executor function has access to `resolve` and `reject` to change the state of the Promise to 'fulfilled' or 'rejected', respectively.
  • 🔗 Promises can be chained using `.then()` and `.catch()` methods, which add Promise reaction records to the Promise object.
  • 📂 The microtask queue is where the asynchronous part of Promises is handled, and it gets priority over the task queue when the call stack is empty.
  • 🔄 When a Promise is resolved, its reaction records' handlers are added to the microtask queue, allowing for non-blocking execution.
  • 🕒 The `setTimeout` function is used in the script to demonstrate asynchronous behavior within a Promise constructor.
  • 🔄 The `.then()` method not only creates a reaction record but also returns a new Promise, enabling Promise chaining.
  • 🔢 The script demonstrates how Promises can be chained to incrementally process results, such as multiplying numbers in sequence.
  • 📈 Real-world applications of Promises might involve image processing, where operations can be chained in a non-blocking manner.
  • 📝 The script concludes with a challenge for viewers to understand the order in which numbers are logged, reinforcing the concept of Promise chaining and execution.

Q & A

  • What is a Promise in JavaScript?

    -A Promise in JavaScript is an object that may produce a single value some time in the future. It represents a proxy for a value not necessarily known when the promise is created. It allows you to associate handlers to be called in the future, when the promise is fulfilled or rejected.

  • How is a Promise created in JavaScript?

    -A Promise is created by using the `new Promise` constructor, which takes an Executor function as an argument. This Executor function itself takes two arguments, typically named `resolve` and `reject`, which are functions used to resolve or reject the promise respectively.

  • What are the internal states of a Promise?

    -A Promise has three possible states: pending, fulfilled, and rejected. The 'pending' state is the initial state. 'Fulfilled' means that the operation completed successfully, and 'rejected' means that the operation failed.

  • What is the purpose of the resolve function in a Promise?

    -The resolve function is used to resolve a Promise, setting its state to 'fulfilled' and assigning it a resulting value. This function is called when the asynchronous operation completes successfully.

  • What does the reject function do in a Promise?

    -The reject function is used to reject a Promise, setting its state to 'rejected' and assigning it a resulting value that represents the reason for the rejection. This function is called when the asynchronous operation fails.

  • What are Promise reaction records?

    -Promise reaction records are objects that are created when you chain a `then` or `catch` method to a Promise. They contain information about the handlers that will be called when the Promise is fulfilled or rejected.

  • How does the event loop handle Promises?

    -The event loop plays a crucial role in handling Promises. When a Promise is resolved or rejected, its handlers are added to the microtask queue. Once the call stack is empty, the event loop checks the microtask queue and executes any pending microtasks.

  • What is the difference between the microtask queue and the task queue?

    -The microtask queue is where Promise reaction handlers are placed and is processed before the task queue. The task queue, also known as the callback queue or macro task queue, is where callbacks from tasks like timers, network responses, and I/O operations are placed.

  • How can you chain Promises in JavaScript?

    -You can chain Promises by using the `then` method. Each call to `then` returns a new Promise, which can then be followed by another `then`, allowing you to create a sequence of asynchronous operations.

  • What is the significance of the microtask queue in asynchronous JavaScript programming?

    -The microtask queue is significant because it allows for the execution of Promise handlers as soon as possible, without blocking the main thread. This helps in keeping the script responsive and efficient.

  • How does the `setTimeout` function interact with Promises?

    -The `setTimeout` function can be used within a Promise's Executor function to schedule an asynchronous task. When the timer completes, the callback passed to `setTimeout` is executed, which can then resolve or reject the Promise based on the result of the asynchronous task.

Outlines

00:00

🔧 Understanding JavaScript Promises

This paragraph delves into the intricacies of JavaScript promises, which are often perceived as complex but are demystified in this explanation. The author introduces the concept of promises, how they are created using the 'new Promise' constructor, and the internal workings such as the promise state, result, and reactions to fulfillment or rejection. It explains the asynchronous nature of promises, the event loop, and the microtask queue, illustrating how promises are resolved or rejected and how their reactions are handled in a non-blocking manner. The paragraph also touches on chaining promises and the incremental handling of results, providing a foundational understanding of promises in JavaScript.

05:00

🔗 Chaining Promises for Asynchronous Operations

The second paragraph builds on the understanding of promises by demonstrating how they can be chained to handle asynchronous operations incrementally. It describes a code snippet where a new promise is created and resolved immediately, followed by subsequent 'then' handlers that manipulate the resolved values in a sequence, showcasing the promise resolution process and the chaining mechanism. The paragraph emphasizes the non-blocking aspect of promise handling, which allows for maintaining script interactivity and efficiency. It also hints at practical applications, such as image processing, where promises can be used to perform a series of operations in a non-blocking manner. The author concludes with a quiz to test the viewer's comprehension of how the numbers in the code snippet are logged, reinforcing the learning with an interactive element.

Mindmap

Keywords

💡Promise

A Promise in JavaScript is an object representing the eventual completion or failure of an asynchronous operation. It is fundamental to the script's theme, illustrating the asynchronous nature of JavaScript. The video explains how a Promise is created using the 'new Promise' constructor and how it can be in a pending, fulfilled, or rejected state. For example, the script describes creating a Promise with a timeout that eventually fulfills with the string 'done'.

💡Executor function

The Executor function is a crucial part of the Promise constructor in JavaScript. It is a function passed to the 'new Promise' and is immediately invoked, with access to the resolve and reject functions that control the state of the Promise. In the video, the Executor function is used to demonstrate setting a timeout that resolves the Promise, showcasing how asynchronous operations are handled within a Promise.

💡Resolve

Resolving a Promise means moving it from a pending state to a fulfilled state. The 'resolve' function is used within the Executor function to indicate successful completion of an asynchronous operation. The video script uses 'resolve' to transition a Promise's state to 'fulfilled' and to set the result to a specific value, like the string 'done'.

💡Reject

Rejecting a Promise signifies that an error has occurred during the asynchronous operation, moving the Promise from a pending to a rejected state. The 'reject' function is used to handle errors. In the script, 'reject' is mentioned as a way to set the Promise state to 'rejected' and assign an error value, although it is not used in the examples provided.

💡Promise state

The state of a Promise can be pending, fulfilled, or rejected. This concept is central to understanding how Promises work in JavaScript. The video script explains that the state is initially pending and can be changed to fulfilled or rejected based on the outcome of the asynchronous operation, such as a successful timeout callback.

💡Promise result

The result of a Promise is the value that it holds once it is fulfilled. It is a key concept in the script, as it demonstrates the outcome of the asynchronous operation. For instance, when a Promise is resolved, the 'promise result' is set to the value passed to the 'resolve' function, like the string 'done' in the video's examples.

💡Promise reaction records

Promise reaction records are objects that are created when the 'then' or 'catch' method is called on a Promise. They contain handlers for the next steps in the Promise chain. The video script explains that these records are crucial for chaining Promises and handling their results asynchronously, as seen when the 'then' method is used to create a reaction record with a handler that logs the result.

💡Microtask queue

The microtask queue is a JavaScript execution context where microtasks, such as Promise reactions, are placed to be executed. The video script emphasizes the microtask queue as part of the event loop process, explaining that handlers from resolved Promises are added to this queue and executed once the call stack is empty, ensuring non-blocking asynchronous behavior.

💡Event loop

The event loop is a runtime execution model for JavaScript that manages the execution of call stacks, microtask queues, and task queues. The video script uses the event loop to explain how asynchronous operations are handled in JavaScript, highlighting its role in checking the microtask queue before moving to the task queue when the call stack is empty.

💡Chaining

Chaining in the context of Promises refers to linking multiple Promises together using the 'then' method. This allows for sequential asynchronous operations. The video script demonstrates chaining with a series of 'then' calls that each process the result of the previous Promise, multiplying the result incrementally, and logging the final value.

💡Asynchronous task

An asynchronous task is an operation that is performed in the background, allowing the main program to continue running without waiting for the task to complete. The video script mentions asynchronous tasks such as reading from a file system, making a network request, or using a timer, which are used within the Executor function to resolve or reject a Promise based on the task's outcome.

Highlights

JavaScript Promises can be intimidating but are not as complicated as they seem when understood.

A Promise is created using the new Promise constructor with an Executor function.

The Executor function has access to resolve and reject methods to handle the Promise's state.

Promises have internal slots including state, result, and reaction records for handling.

Promise reactions are created by chaining then or catch methods, which include handler callbacks.

The event loop and microtask queue are key to the asynchronous behavior of Promises.

Resolving a Promise updates its state and result, and schedules the handler to the microtask queue.

The microtask queue is prioritized over the task queue in the event loop for asynchronous operations.

Asynchronous tasks like network requests can resolve or reject a Promise based on their outcome.

The then method not only creates a reaction record but also returns a new Promise for chaining.

Chaining then methods allows for incremental handling of Promise results in a non-blocking way.

Promises can be used to perform a series of operations in sequence without blocking the main thread.

The transcript provides a step-by-step walkthrough of how Promises are executed and handled.

The example given demonstrates how Promises can be used for image processing tasks.

The transcript aims to demystify Promises and provide a clear understanding of their inner workings.

The speaker provides additional resources, including a blog post and a link to the ECMAScript specification.

A front-end Master course is mentioned for further exploration of JavaScript internals.

Transcripts

play00:00

promises in javascripts are known to be

play00:01

a little daunting intimidating annoying

play00:04

I don't know whatever negative thing you

play00:05

want to say about them but I promise you

play00:08

that once you understand what happens

play00:10

behind the scenes under the hood they're

play00:11

actually not that complicated so today I

play00:14

want to walk you through promise

play00:15

execution and see what happens behind

play00:17

the scenes when we interact and work

play00:19

with promises so one way to create a

play00:21

promise is by using the new promise

play00:23

Constructor and this Constructor also

play00:26

receives an Executor function now when

play00:28

the new promised Constructor is executed

play00:30

a new promis object is created in memory

play00:32

and this object contains some internal

play00:34

slots like the promis state promis

play00:36

result promis fulfill reactions promise

play00:39

reject reactions and promise is handled

play00:41

we also get some additional

play00:43

functionality to either resolve or

play00:45

reject this promise now we can resolve

play00:47

this Promise by calling resolve which is

play00:49

made Available To Us by the executor

play00:51

function and when we call resolve the

play00:53

promise state is set to fulfilled and

play00:55

the promised result is set to the value

play00:57

that we pass to resolve so the string

play00:59

done in the this case similarly we can

play01:01

reject the Promise by calling reject in

play01:03

which case the promise state is said to

play01:05

rejected and the promise result is set

play01:08

to the value that we passed to reject so

play01:11

the string fail cool nothing special

play01:13

here we're just calling a function to

play01:15

change some object property so what's so

play01:16

special about promises well that's

play01:18

actually in those two fields that we

play01:20

skipped so far so in the promise fulfill

play01:22

reactions and the promise reject

play01:24

reactions because these fields contain

play01:26

something called Promise reaction

play01:28

records we can create a promise reaction

play01:30

record by chaining a then or a catch

play01:32

method to the promise so whenever we

play01:34

chain then the then method is

play01:36

responsible for creating that promise

play01:38

reaction record and among many other

play01:40

fields this reaction record contains a

play01:43

hand layer and this has some code and

play01:45

that code is that call back that we

play01:46

passed to then now what happens is that

play01:49

whenever we resolve the promise so we

play01:52

call resolve resolve is added to the

play01:54

call stack the promis state is set to

play01:56

fulfill the promised result is set to

play01:58

the value that we pass to resolve and

play02:00

the promise reaction records Handler

play02:03

receives that promis result so the

play02:05

string done in this case and the Handler

play02:07

is now added to the microtask CU this is

play02:10

where the asynchronous part of promises

play02:12

comes into play and just kind of as a

play02:14

quick refresher um whenever the call

play02:16

stack is empty the event Loop first

play02:18

checks in the microtask que and whenever

play02:20

this queue is empty it goes to the task

play02:22

que also called like the Callback Q

play02:25

macro task Q whatever you want to call

play02:26

it what's important here is that the

play02:28

microtask Q gets PR now so far I've only

play02:31

been calling resolve and reject

play02:32

synchronously like right in the promise

play02:34

Constructor but usually you want to

play02:36

initiate some kind of asynchronous task

play02:38

in this Constructor with an asynchronous

play02:41

task I mean anything off the main threat

play02:42

So reading something from a file system

play02:45

or a network request or something as

play02:47

simple as a timer whenever they return

play02:50

that data we can use their callback

play02:52

function to either resolve with the data

play02:54

that they returned or reject if an error

play02:57

occurred so just to keep it simple let's

play03:00

uh see how the execution goes for This

play03:02

Promise Constructor so we have the

play03:03

promise Constructor that has a set

play03:05

timeout and we also have a then Handler

play03:08

let's just go through it step by step

play03:09

and see what happens so first the new

play03:11

promise Constructor is added to the call

play03:13

stack and this creates the promise

play03:15

object the executor function is called

play03:18

and on the first line we have a set

play03:19

timeout so set timeout is added to the

play03:21

call stack and this one is responsible

play03:23

for scheduling that timer in this case

play03:25

100 milliseconds and this has that call

play03:27

back that we passed to set time out so

play03:29

the function that eventually calls

play03:31

resolve then on the next line we have

play03:33

the den Handler so then is added to the

play03:35

call stack and this is responsible for

play03:37

creating that promis reaction record so

play03:39

this creates a promise reaction record

play03:41

with a call back that we provided as its

play03:43

Handler now then is popped off the call

play03:45

stack and let's just imagine that those

play03:47

100 milliseconds are up now so the call

play03:49

back that we passed to set timeout is

play03:51

now added to the task Q there's nothing

play03:53

on the call stack anymore script is

play03:55

finished so it can now go from the task

play03:57

Q to the call stack and this now calls

play03:59

result so this changes the promise state

play04:01

to fulfilled the promise result to the

play04:03

string done and it schedules that

play04:05

Handler to the microtask que result is

play04:07

now done popped off the call stack so is

play04:09

the call back and there's again nothing

play04:11

on the call stack so the event Loop

play04:13

first checks the microtas Q while there

play04:15

are Handler is waiting so the Handler is

play04:17

added to the call stack and this then

play04:19

console logs the promised results which

play04:21

is the string done now the nice thing

play04:23

about the fact that it's added to the

play04:24

microtask queue is that in the meantime

play04:26

our script can just keep running it can

play04:28

just keep performing important tasks and

play04:30

it stays interactive only when the call

play04:32

stack is empty so when there's like

play04:33

nothing important to do does it get

play04:36

added to the call stack from the micras

play04:37

Q so this means that we can handle the

play04:39

promise result in a nonblocking way now

play04:43

another cool thing is that then itself

play04:44

also returns a promise so besides just

play04:46

creating that promise reaction record it

play04:48

also creates a promise object and this

play04:51

allows us to kind of chain those vents

play04:53

to each other and have this incremental

play04:56

promise results um hand

play05:00

so let's just see what happens when we

play05:02

have this code snippet so the first we

play05:04

have the new promise Constructor which

play05:05

creates that promise object this

play05:07

immediately resolves with one so the

play05:09

state is set to fulfilled and promised

play05:10

result is one then we call the then

play05:13

Handler so this creates a promised

play05:14

reaction record with with the Handler

play05:16

being results and then it returns result

play05:18

time two result being that promised

play05:20

result which is the number one but it

play05:21

also creates a promis object and this is

play05:23

now set to fulfilled because we returned

play05:25

result time two result being one so

play05:28

result time 2 is two and we do the same

play05:30

on the next line so we have another then

play05:32

this creates a promise reaction record

play05:34

again with the exact same Handler so

play05:36

result times two this time result being

play05:38

two so 2 * 2 is four so This Promise

play05:42

result is now four and then we have one

play05:44

more and this just logs that value so

play05:47

again the state is set to fulfilled in

play05:49

this case the result is undefined

play05:50

because we didn't return a value we're

play05:52

only logging it but in the console you

play05:55

will see four so that's just something

play05:57

to keep in mind we can chain those dens

play05:59

together and kind of incrementally

play06:01

handle that promise result in a

play06:02

non-blocking way now of course in a real

play06:05

application you won't use numbers like

play06:07

this but instead you want to

play06:09

incrementally handle that promis result

play06:12

maybe you have some kind of image that

play06:13

you first want to resize and then add a

play06:15

filter and then change the format I

play06:18

don't know what you want to do with an

play06:19

image but you can do all that by

play06:20

chaining this then in a nonblocking way

play06:23

and that's pretty cool that's pretty

play06:25

pretty powerful all right so let's see

play06:27

how well you understand promises or at

play06:29

least how well I explained it so pause

play06:31

this video real quick and see if you

play06:33

know how these numbers get loged all

play06:36

right so if you guessed 132 then

play06:38

congrats you understood it perfectly or

play06:40

it's a lucky guess I don't know but

play06:42

let's see how this happened so first we

play06:44

have the new promise Constructor again

play06:45

added to the call stack new promise

play06:47

object is created then we have the

play06:49

executor function which gets added to

play06:50

the call stack and on the very first

play06:52

line we have console log one this gets

play06:54

added through the call stack and this

play06:56

logs one then we call resolve with two

play07:00

so now the promise state is changed to

play07:02

fulfilled the promise result is set to

play07:04

two and we don't have a promise fulfill

play07:07

reaction yet that only happens on the

play07:09

next line resolve is popped off the call

play07:11

stack so is the executor function and

play07:13

the new promise Constructor now on the

play07:14

next line we finally have then so this

play07:16

creates that promise reaction record um

play07:19

it doesn't get added to that list

play07:20

because the promise is already resolved

play07:22

this would just take up unnecessary

play07:23

memory but it still has access to that

play07:25

promised result so this promised

play07:26

reaction record has the Handler with the

play07:29

result being two and then console log

play07:31

result so this is immediately added to

play07:33

the microtask CU it's important to

play07:35

remember that it's not immediately

play07:36

executed no it is immediately scheduled

play07:39

to the microtask CU then we go to the

play07:41

next line because our script isn't done

play07:43

yet the call stack isn't empty yet and

play07:45

there we just call console log 3 in a

play07:47

normal way so this is added to the call

play07:49

stack logs three so now we have 1 three

play07:52

now finally our script is done there's

play07:53

nothing on the call stack so the first

play07:55

task in the microtask que is added to

play07:57

the call stack which is that then

play07:59

Handler which then console logs the

play08:01

result being two so now finally two gets

play08:04

logged hopefully my explanation kind of

play08:07

helped to demystify Promises at least a

play08:11

little bit um I also have a written blog

play08:14

post in the description if you prefer to

play08:16

just read it uh I also added a a link to

play08:19

the ecospec if you're like me and like

play08:21

to just read the

play08:22

specification for the technologies that

play08:25

we're working with and if you want more

play08:27

kind of questions like this I have a

play08:28

front of Master course also link below

play08:30

where I have a bunch of questions about

play08:32

javascripts internals and also kind of

play08:34

explanations like this one so you can

play08:36

test your JavaScript knowledge and uh

play08:38

maybe learn something new thanks and

play08:40

have fun coding

Rate This

5.0 / 5 (0 votes)

الوسوم ذات الصلة
JavaScriptPromisesAsynchronousCallbacksMicrotasksEvent LoopExecutor FunctionResolveRejectNon-blockingCoding Tutorial
هل تحتاج إلى تلخيص باللغة الإنجليزية؟