Original text: jakearchibald.com/2015/tasks-…

Translator: Front-end wisdom

Click “like” and then look, wechat search [Big Move the world] pay attention to this person without dACHang background, but with a positive attitude upward. In this paper, making github.com/qq449245884… Has been included, the article has been categorized, also organized a lot of my documentation, and tutorial materials.

Everyone said there was no project on your resume, so I found one and gave it away【 Construction tutorial 】.

Consider the following JavaScript code:

console.log('script start');

setTimeout(function() {
  console.log('setTimeout');
}, 0);

Promise.resolve().then(function() {
  console.log('promise1');
}).then(function() {
  console.log('promise2');
});

console.log('script end');
Copy the code

What is the order in which the console prints?

The answer

The correct answer is :script start, script end, promise1, promise2, setTimeout, but the results are inconsistent due to different browser implementations.

Microsoft Edge, Firefox 40, iOS Safari, and Desktop Safari 8.0.8 print setTimeout before printing PromisE1 and Promise2 — this seems to be a competing implementation. This is really strange because Firefox 39 and Safari 8.0.7 always turn out to be correct.

Why is that?

To understand this, you need to understand how the ** event loop ** handles tasks and microtasks.

Each “thread” has its own event loop, so each Web worker has its own event loop and can execute independently, while all Windows from the same domain share an event loop, so they can communicate synchronously.

The event loop continues until the Tasks queue is empty. An event loop has multiple task sources that guarantee the order of execution in that source (for example, IndexedDB defines their own specifications), but the browser can choose which source to execute the task in each loop. This allows the browser to prioritize performance-sensitive tasks, such as user input.

Tasks are placed in the task source so that the browser can access the JavaScript/DOM domain from within and ensure that these operations are done in sequence. During Tasks execution, the browser may update the render. Moving from mouse click to event callback requires scheduling a task, as does parsing HYPERtext Markup Language.

SetTimeout delays a given time and then schedules a new task for its callback. This is why setTimeout is printed after printing script end, because printing script End is part of the first task, whereas setTimeout is in a separate task.

** Microtasks ** are typically scheduled for things that should happen immediately after the script is currently executed, such as responding to a batch of operations, or processing asynchronously without affecting the entire new task.

As long as no other JavaScript is in mid-execution and at the end of each task, the microtask queue is processed after the callback. Any other microtasks that are queued during a microtask are added to the end of the queue and processed. Microtasks include MutationObserver callbacks. For example, the Promise callback in the example above.

A Promise in a Settled state, or one that has become settled (asynchronous requests being settled), will immediately put its callback (then) into the microtask queue.

This ensures that the promise callback is asynchronous, even if the promise has become settled. So a settled Promise call to.then(Yey, NAY) will immediately add a microtask to the microtask queue.

This is why promise1 and promise2 print after script end, because the currently running script must complete before processing the microtask. Promise1 and promise2 print before setTimeout because microtasks always occur before the next task.

Ok, step by step:

What are the differences between browsers?

Some browsers print in script start, script end, setTimeout, promise1, promise2. They run the Promise callback after setTimeout. It’s likely that they invoked the Promise callback as part of a new task, not as a microtask.

This is also understandable, since promises come from ECMAScript, not HTML. ECMAScript has the concept of “jobs,” similar to microtasks, but the relationship is not clear beyond vague mailing list discussions. However, there is general agreement that promises should be part of the microtask queue and for good reason.

Treating promises as tasks can cause performance problems because callbacks need not be delayed for task-related things like rendering. It also leads to nondeterminism due to interactions with other task sources, and may interrupt interactions with other apis, more on that later.

Here is an Edge feedback that incorrectly identifies Promises as a task. WebKit nightly does it right, so I think Safari will eventually be fixed, and Firefox 43 seems to be fixed.

How do you tell if something uses tasks or microtasks

Experimenting is one way to see how it prints relative to promises and setTimeout, although this depends on the implementation being correct.

One way is to look at the specification: queue a task: Step 14 of setTimeout

Step 5 of queuing a mutation record

As mentioned above, ECMAScript calls a microtask a job: EnqueueJob is called to queue a microtask: step 8. A of PerformPromiseThen

Level 1 boss fight monsters

Here is the HTML code:

<div class="outer">
  <div class="inner"></div>
</div>
Copy the code

Given the following JS code, what will be printed if you click on div.inner?

// Let's get hold of those elements var outer = document.querySelector('.outer'); var inner = document.querySelector('.inner'); // Let's listen for attribute changes on the // outer element new MutationObserver(function() { console.log('mutate'); }).observe(outer, { attributes: true }); // Here's a click listener... function onClick() { console.log('click'); setTimeout(function() { console.log('timeout'); }, 0); Promise.resolve().then(function() { console.log('promise'); }); outer.setAttribute('data-random', Math.random()); } / /... which we'll attach to both elements inner.addEventListener('click', onClick); outer.addEventListener('click', onClick);Copy the code

Try it before you peek at the answers

Give it a try

Is it different from what you thought? If so, your results are probably correct as well. Unfortunately, browser implementations are not uniform, so here are the results for each browser:

Who is right?

Scheduling the ‘click’ event is a task. Mutation Observer and promise callbacks are listed as microtasks. SetTimeout callback column task. So the running process is as follows:

So Chrome is right. What’s new to me is that the microtask runs after the callback (as long as no other Javascript is running), which I thought would only be executed at the end of a task.

What’s wrong with the browser?

For mutation Callbacks, Firefox and Safari both performed correctly between the internal and external area click events, clearing the microtask queue, but Promises queue handling looks different from Chrome. This is somewhat understandable, since the relationship between jobs and microtasks is unclear, but I still expect Firefox ticket. Safari ticket to be processed between event callbacks.

For Edge, we have seen that it incorrectly treats Promises as tasks. It also does not clear the microtask queue between click callbacks, but after all click callbacks have been executed, so there is only one mutate printed after two clicks.

Level 1 boss attack upgrade

Again using the above example, what if we ran the following code:

inner.click();
Copy the code

As before, it fires the click event, but this time through JS.

Give it a try

Here’s how each browser works:

I swear I’ve been getting different results from Chrome, I’ve updated this chart many times and I thought I was incorrectly testing Canary. If you get different results in Chrome, let me know which version in the comments.

Why is it different?

It should look something like this:

So the correct order is: click, click, promise, mutate, promise, timeout, timeout, it seems Chrome is right.

Previously, this meant that microtasks ran between listener callbacks, but.click() would cause events to be scheduled synchronously, so the script calling.click() would still be on the stack between callbacks. The above rules ensure that microtasks do not interrupt JavaScript mid-execution. This means that we do not process the queue of microtasks between listener callbacks, they are processed after two listeners.

conclusion

Tasks are executed sequentially and the browser can render between them:

Microtasks are executed sequentially and perform:

  • After each callback, as long as no other code is running.

  • At the end of each task.

The bugs that may exist after code deployment cannot be known in real time. In order to solve these bugs, I spent a lot of time on log debugging. Incidentally, I recommend a good BUG monitoring tool for youFundebug.

communication

This article is updated every week, you can search wechat “big move the world” for the first time to read and urge more (one or two earlier than the blog hey), this article GitHub github.com/qq449245884… It has been included and sorted out a lot of my documents. Welcome Star and perfect. You can refer to the examination points for review in the interview.