Amazing! Visual JS: dynamic diagram demonstrates the process of promises & async / await!

Time:2020-9-27

Amazing! Visual JS: dynamic diagram demonstrates the process of promises & async / await!

  • Original address: https://dev.to/lydiahallie/javascript-visualized-promises-async-await-5gke
  • Original author: Lydia Hallie

Why

Have you ever run JS code that doesn’t work as expected?

For example, a function is executed at random, unpredictable time, or delayed.

At this point, you need to introduce a very cool new feature from ES6: promise to deal with your problems.

In order to understand promise deeply, I made some animations to demonstrate the operation of promise one sleepless night. My curiosity for many years was finally realized.

With promise, why do you use it, how it works at the bottom, and how do we write it in the most modern way?

introduce

When writing JavaScript, we often have to deal with tasks that depend on other tasks!

For example: we want to get an image, compress it, apply a filter, and save it.

First, use the getimage function to get the image we want to edit.

Once the image is loaded successfully, the image value is passed to an ocmpressimage function.

When the image has been successfully resized, apply a filter to the image in the applyfilter function.

After the image is compressed and filter added, save the image and print the successful log!

Finally, the code is very simple, as shown in the following figure:

Amazing! Visual JS: dynamic diagram demonstrates the process of promises & async / await!

Did you notice? Although the above code can also get the results we want, the completion process is not friendly.

A large number of nested callback functions are used, which makes our code particularly difficult to read.

Because many nested callback functions are written, these callback functions depend on the previous callback function, which is often called callback hell.

Fortunately, promise in ES6 can handle this situation very well!

Let’s take a look at what promise is and how it can help us in situations like this.

Promise syntax

ES6 introduces promise. In many tutorials, you may read something like this:

Promise is a placeholder for a value that either resolves or rejects at some point in the future.

For me, such an explanation never makes things clearer.

In fact, it just makes me feel that promise is a strange, vague and unpredictable piece of magic.

Let’s take a look at what promise really is?

We can create a promise using a promise constructor that receives a callback function.

Cool, let’s try it!

Amazing! Visual JS: dynamic diagram demonstrates the process of promises & async / await!

Wait, what’s the return value you just got?

Promise is an object that contains a statePromiseStatusAnd a valuePromiseValue

In the example above, you can see thatPromiseStatusThe value of is pending,PromiseValueThe value of is undefined.

However – you will never interact with this object, you won’t even be able to access itPromiseStatusandPromiseValueThese two attributes!

However, when using promise, the values of these two properties are very important.


PromiseStatusThat is to sayPromiseCan be one of the following three values:

  • fulfilled: promiseHas beenresolved。 Everything’s fine. InpromiseNo internal errors occurred.
  • rejected: promiseHas beenrejected。 Oh, something went wrong.
  • pending: promiseIt has not been resolved or rejected for the time being, and it is still in thependingstate

Well, it all sounds great, but whenpromiseThe state ofpendingfulfilledorrejectedWhy is this state important?

In the example above, we just wanted toPromise The constructor passes a simple callback function() => {}

However, this callback function actually takes two parameters.

  • The value of the first parameter is often calledresolveorresIt’s a function in thePromiseIt should be solvedresolveWill be called.
  • The value of the second parameter is often calledrejector rejIt’s also a function in thePromiseSome mistakes should be rejectedrejectIs called.

Amazing! Visual JS: dynamic diagram demonstrates the process of promises & async / await!

Let’s try to see when we callresolveorrejectMethod.

In my case, putresolveThe method is calledres, putrejectThe method is calledrej

Amazing! Visual JS: dynamic diagram demonstrates the process of promises & async / await!

Excellent! We finally know how to get rid of itpendingStatus andundefinedIt’s worth it!

  • When we callresolveMethod,promiseThe state offulfilled
  • When we callrejectMethod,promiseThe state ofrejected

Interestingly, I had Jake Archibald proofread this article, and he actually pointed out that there was an error in chrome that currently displays the status as “fulfilled” instead of “resolved.”. Thanks to Mathias bynens, which is now fixed in Canary!
Amazing! Visual JS: dynamic diagram demonstrates the process of promises & async / await!

Well, now we know how to better control that fuzzy promise object. But what was he used to do?

In the previous introduction section, I showed an example of getting an image, compressing it, applying a filter to it and saving it! In the end, this becomes a messy nested callback.

lucky,PromiseCan help us solve this problem!

First, let’s rewrite the entire block of code so that each function returns onePromiseTo replace the previous function.

If the image is loaded and everything is OK, let’s use the loaded image to solve the problem (resolve)promise

Otherwise, if there is an error somewhere when loading the file, we will reject it with the error that occurred(reject)promise

Amazing! Visual JS: dynamic diagram demonstrates the process of promises & async / await!

Let’s take a look at what happens when we run this code on the terminal?

Amazing! Visual JS: dynamic diagram demonstrates the process of promises & async / await!

Very cool! As we expected,promiseThe value of the parsed data is obtained.

But now? We don’t care about the whole thingpromiseObject, we only care about the value of the data! Fortunately, there are built-in methods to get itpromiseValue of.

For apromiseWe can use the above three methods:

  • .then (): after a promise is called by resolved.
  • . catch(): called after a promise is rejected
  • . finally(): whether promise is resolved or rejected is always called

Amazing! Visual JS: dynamic diagram demonstrates the process of promises & async / await!

. then method receives and passes toresolveMethod.

Amazing! Visual JS: dynamic diagram demonstrates the process of promises & async / await!

. catch method receives and passes torejectedMethod.

Amazing! Visual JS: dynamic diagram demonstrates the process of promises & async / await!

In the end, we havepromiseAfter being solved(resolved)You don’t need the entirepromiseObject!

Now we can do whatever we want with this value.


By the way, when you know onepromisealwaysresolveOr alwaysrejectYou can writePromise.resolveorPromise.reject, pass in what you wantrejectorresolveOfpromiseValue of.

Amazing! Visual JS: dynamic diagram demonstrates the process of promises & async / await!

You’ll see this grammar a lot in the examples below.

In the getimage example, in order to run them, we eventually had to nest multiple callbacks. lucky,.thenProcessors can help us do this!

.thenIts own implementation results in apromise。 This means that we can link any number of.then: PreviousthenThe result of the callback is passed as a parameter to the nextthenCallback!

Amazing! Visual JS: dynamic diagram demonstrates the process of promises & async / await!

In the getimage example, in order to pass the processed image to the next function, we can link multiplethenCallback.

Instead of getting many nested callbacks at the end of the day, we’ve got a neat onethenChain.

Amazing! Visual JS: dynamic diagram demonstrates the process of promises & async / await!

Perfect! This syntax looks much better than the previous nested callback.

Macro task and micro task

We know something about how to create itpromiseAnd how to extract itpromiseMethod of the value of the.

Let’s add some more code to the script and run it again:

Amazing! Visual JS: dynamic diagram demonstrates the process of promises & async / await!

Wait, what happened?!

first,Start!Is output.

Well, we’ve seen the news that’s coming:console.log('Start!')Output in the first line!

However, the second value to be printed isEnd!It’s notpromiseSolved value! Only inEnd!After being printed,promiseValue will be printed.

What’s going on here?

We finally saw itpromiseReal power! althoughJavaScriptIt’s single threaded. We can use itPromiseAdd asynchronous task!

Wait, haven’t we seen this before?

In JavaScript event loop, we can not also use browser native methods such assetTimeoutCreate some kind of asynchronous behavior?

yes! However, within the event loop, there are actually two types of queues:Macro queue(or just calledTask queue)AndMicro task queue

(macro) task queue forMacro task, micro task queue forMicro task

So what are macro tasks and what are micro tasks?

Although there are more of them than I’ve described here, the most commonly used ones are shown in the table below!

| | | | |
| :——: | :——: | :——: | :——: |
| (Macro)task: | setTimeout | setInterval | setImmediate |
| Microtask: | process.nextTick | Promise callback | queueMicrotask |

We seePromiseIn the micro task list! Be aPromiseSolve(resolve)And call itsthen()catch()orfinally()Methods, the callback functions in these methods are added to the micro task queue!

This means thatThen (), chat () or finally ()The callback function within the method is not executed immediately. It is essentially for usJavaScriptCode added some asynchronous behavior!

So when to execute the callback in then (), catch (), or finally()?

Event loops give different priorities to tasks:

  1. All functions currently in the call stack are executed. When they return a value, they are ejected from the stack.
  2. When the call stack is empty, all the queued micro tasks will pop up one by one from the micro task task queue into the call stack, and then be executed in the call stack! (the micro task itself can return a new micro task, effectively creating an infinite micro task cycle)
  3. If both the call stack and the microtask queue are empty, the event loop checks to see if there are any tasks in the macro task queue. If there are tasks in the macro task, it will pop up from the macro task queue and enter the call stack. After being executed, it will pop up from the call stack!

Let’s take a quick look at a simple example:

  • Task 1: functions that are immediately added to the call stack, such as calling it immediately in our code.
  • Task2, task3, task4: Micro tasks, such aspromiseinthenMethod, or use thequeueMicrotaskAdd a task for.
  • Task5, task6: macro tasks, such asSetTimeout or setimmediateCallback in

Amazing! Visual JS: dynamic diagram demonstrates the process of promises & async / await!

First, Task1 returns a value and pops up from the call stack. then,JavaScriptThe engine checks the queued tasks in the micro task queue. Once all the tasks in the micro task are put into the call stack and finally popped up, the JavaScript engine will check the tasks in the macro task queue, pop them into the call stack, and pop them out of the call stack when they return values.

The box with enough pink in the picture is a different task. Let’s use some real code to use it!

Amazing! Visual JS: dynamic diagram demonstrates the process of promises & async / await!

In this code, we have the then callback of the macro task setTimeout and the micro task promise.

Once the JavaScript engine reaches the line where the setTimeout function is located, the event loop is involved.

Let’s run this code step by step to see what kind of log we get!

Quick to mention: in the example below, I’m showing the imageconsole.logsetTimeoutandPromise.resolveMethods are being added to the call stack. They are internal methods that don’t actually appear in the stack trace, so if you’re using a debugger, don’t worry, you won’t see them anywhere. It just makes the concept easier to explain without adding a bunch of sample file code.

On the first line,JavaScriptThe engine encounteredconsole.log()Method, which is added to the call stack, and then outputs the value start! On the console!.console.logFunction from the call stack, afterJavaScriptThe engine continues to execute the code.

Amazing! Visual JS: dynamic diagram demonstrates the process of promises & async / await!

JavaScriptThe engine encounteredsetTimeoutMethod, he is popped into the call stack.setTimeoutIs the browser’s native method: its callback function(() => console.log('In timeout'))Will be added toWeb APIUntil the timer finishes. Although we provide a value of 0 for the timer, it is added to the macro task queue(setTimeoutIs it a macro task) after which the callback is pushed in firstWeb API

Amazing! Visual JS: dynamic diagram demonstrates the process of promises & async / await!

JavaScriptThe engine encounteredPromise.resolvemethod.Promise.resolveIs added to the call stack. stayPromiseSolve(resolve)After value, itsthenThe callback function in is added to the micro task queue.

Amazing! Visual JS: dynamic diagram demonstrates the process of promises & async / await!

JavaScriptThe engine sees that the call stack is now empty. Since the call stack is empty, it will check whether there are queued tasks in the micro task queue! Yes, there are tasks in line,promiseOfthenThe callback function in is waiting for its turn! It’s popped into the call stack, and then it’s outputpromiseAfter being solved(resolved)The value of: the string in this examplePromise!

Amazing! Visual JS: dynamic diagram demonstrates the process of promises & async / await!

JavaScriptThe engine sees that the call stack is empty, so if the task is queued, it will check the microtask queue again. At this point, the micro task queue is completely empty.

It’s time to check the macro task queue:setTimeoutCallback is still waiting there!setTimeoutIs popped into the call stack. Callback function returnconsole.logMethod to output a stringIn timeout!setTimeoutThe callback pops up from the call stack.

Amazing! Visual JS: dynamic diagram demonstrates the process of promises & async / await!

Finally, everything’s done! It seems that the output we’ve seen before is not so unexpected in the end.

Async/Await

ES7It introduces a newJavaScriptAdd asynchronous behavior to thepromiseIt’s easier to use! along withasyncandawaitKeyword, we can create an implicit returnpromiseOfasyncFunction. But what should we do?

Before, we saw that whether it’s through inputnew Promise(() => {})Promise.resolveorPromise.rejectWe can all use it explicitlyPromiseobjects creatingpromise

We can now create asynchronous functions that implicitly return an object instead of explicitly using itPromiseObject! That means we don’t have to write anything anymorePromiseYes.

Amazing! Visual JS: dynamic diagram demonstrates the process of promises & async / await!

althoughasyncImplicit return of functionpromiseIt’s a great fact, but it’s in useawaitOnly when the keyword is displayedasyncThe real power of functions. When we waitawaitThe value after returns aresolvedOfpromiseThroughawaitKeyword, we can pause asynchronous functions. If we want thisresolvedOfpromiseLike we used beforethenCall back so that we can beawaitOfpromiseThe value of is assigned as a variable!

So can we pause an asynchronous function? Good, but what does that mean?

When we run the following block of code, let’s see what happens:

Amazing! Visual JS: dynamic diagram demonstrates the process of promises & async / await!

Well, what’s going on here?

Amazing! Visual JS: dynamic diagram demonstrates the process of promises & async / await!

first,JavaScriptThe engine encounteredconsole.log。 It’s popped into the call stack, after thatBefore function!Is output.

Amazing! Visual JS: dynamic diagram demonstrates the process of promises & async / await!

Then, we call the asynchronous functionmyFunc()After thatmyFuncFunction body running. On the first line in the body of the function, we call anotherconsole.log, this time a string is passed inIn function!console.logIs added to the call stack, outputs the value, and then pops up from the stack.

Amazing! Visual JS: dynamic diagram demonstrates the process of promises & async / await!

The body of the function continues, taking us to the second line. In the end, we saw oneawaitkeyword!

The first thing that happens is the execution of the waiting value: in this case, the functionone。 It is popped into the call stack and eventually returns a resolved statepromise。 oncePromiseSolved andoneReturns a value,JavaScriptYesawaitkeyword.

When you meetawaitThe asynchronous function is suspended. The execution of the function body is suspended,asyncThe rest of the code in the function will run in a micro task instead of a regular task!

Amazing! Visual JS: dynamic diagram demonstrates the process of promises & async / await!

Now, because I metawaitKeywords, asynchronous functionsmyFuncSuspended,JavaScriptThe engine jumps out of the asynchronous function and continues to execute code in the execution context in which the asynchronous function is called: in this caseGlobal execution context! ‍♀️

Amazing! Visual JS: dynamic diagram demonstrates the process of promises & async / await!

In the end, there are no more tasks running in the global execution context! Event loop check to see if there are any micro tasks in the queue: Yes, there are! It’s solvedoneAfter the value of the asynchronous functionmyFuncStart queuing.myFuncIs popped into the call stack and continues to run where it was interrupted before.

variableresFinally, we get its value, that isoneReturnedpromiseSolved value! We use itresIn this case, it is a stringOne!)Callconsole.logOne!Is printed to the console andconsole.logPop up from the call stack.

In the end, everything is done! You noticeasyncFunction compared topromiseOfthenWhat’s the difference?awaitKeyword pauseasyncFunction, however, if we use thethenAnd then,PromiseThe subject of will continue to be executed!

Well, that’s quite a lot of information! When usingPromiseIf you still feel a little overwhelmed, don’t worry at all. I personally think that when using asynchronousJavaScriptWhen you need experience to pay attention to patterns, you will feel confident.

When using asynchronousJavaScriptI hope that the “unpredictable” or “unpredictable” behavior you may encounter will become more meaningful now!

last

The way and style of language expression of foreign friends’ technical blogs are quite different from those of Chinese people.

Every time I see a long or awkward sentence, I want to write one in my own language

Maybe I can write one more quickly than translate it

Amazing! Visual JS: dynamic diagram demonstrates the process of promises & async / await!

Recommended reading:

  1. Through 10 examples of small exercises, quickly get familiar with the core new features of vue3

2. Heavyweight: summary of open source projects of 100k + star on GitHub

  1. Highly recommended: data structure and Algorithm Project of 170k + star front end learning on GitHub
  2. A diagram clarifies the responsive system of Vue 3.0 and realizes the reduced version of responsive system
  3. Black learning style: how to find excellent open source projects

Amazing! Visual JS: dynamic diagram demonstrates the process of promises & async / await!

Give me your support

Recommended Today

Explain idea git branch backoff specified historical version

scene When I submitted this modification to the local and remote branches, I found that there were still some changes missing in this submission, or this modification was totally wrong, but I also pushed it to the remote repository. How to go back? problem How can the content that has been submitted to the repository […]