Relearning the JavaScript event loop in an instance

  • 2021-10-11 17:39:34
  • OfStack

Single-threaded JS

As we all know, js is a single-threaded language, that is, it can only do one thing in the same time. Why js is single-threaded is mainly related to its purpose.

As a browser scripting language, js is primarily used to interact with users & Operating DOM, we don't want to operate DOM in parallel. If it is not a single thread, one of our threads adds content to the DOM node, but the other thread deletes this node. Which one should prevail?

Therefore, in order to avoid complexity, JavaScript is single-threaded from the birth of 1.

Event Loop (event loop)

JS is a single-threaded language, meaning that code is executed line by line. All tasks must be queued, and the last one will not be executed until the first one is finished.

However, ajax, setTimeOut, promise, which are commonly used in development, do not block the process. If the browser consists of only one js engine, the browser will block when encountering these time-consuming requests or operations, which is definitely not what we want.

In fact, js single thread means that the browser has only one thread when interpreting and executing js code, that is, js engine thread. But the browser also includes 1 other thread to handle these asynchronous methods, such as Web APIs thread, GUI rendering thread and so on.

Handling flow of event loop:

The JS thread relies on the call stack to process and execute the js code, and when it encounters some asynchronous operations, it hands them over to Web APIs and continues on its own.
The Web APIs thread adds the received events to the task queue according to the rule and order determined by 1.
When the JS thread processes all the current tasks (that is, the execution stack is empty), it checks whether there are events waiting to be processed in the task queue. If there are, it takes out one event callback and puts it into the execution stack for execution.
Then keep repeating step 3.

Macro task and micro task

Task queues are divided into macro task queues and micro task queues:

Macro task queue (macrotask queue): setTimeout, setInterval, setImmediate, I/O, UI rendering, etc. Micro-task queue (microtask queue): Promises, Object. observe, MutationObserver, process. nextTick, etc.

Therefore, we refine the processing flow of the event loop under 1 (browser environment):

The JS thread relies on the call stack to process and execute the js code. When it encounters some asynchronous operations, it hands them over to Web APIs and continues on its own.
The Web APIs thread adds the received events to the task queue according to the rule and order defined by 1. Macro task events are added to the macro task queue, and micro task events are added to the micro task queue.
The JS thread completes all current tasks (that is, the execution stack is empty), First, go to the micro-task queue to check whether there are events to be processed. If there are, all events in the micro-task queue will be executed one by one until the micro-task queue is empty, then go to the macro task queue to take out the first event for execution, and then check whether there are events to be processed in the micro-task queue after executing this macro task event.
Then keep repeating step 3.

Re-learning JavaScript event cycle in actual demand

What is an JS event loop?

This question is often asked in autumn recruitment, but my understanding is limited to the above, and then I have brushed several questions about the order of output values, without any practical application scenarios in business. Later, after getting offer, I forgot 1 dry and 2 clean, and I didn't have a deeper understanding until I started writing code after graduation.

Background
Users upload multiple pictures, and the front end gets the url and wide incidence of each picture and sends it to the back end.

Solve
The first is to get the file uploaded by the user and do some checksum restrictions


//  Call the method of adding pictures to the system bullet box 
addFile(e) {
 let uploadFiles = e.target.files , self = this;

 self.getListData = []; //  An array of objects to pass to the backend 

 self.testFiles(uploadFiles) //  Do to the file uploaded by the user 1 Some checksum restrictions 
  
 self.loadAll(uploadiles) //  Call loadAll Method 
},

Then let's look at loadAll, mainly traversing these uploaded picture files, and then every picture file calls loadImg


loadAll(files) {
 let promises = [],self = this
 
 //  Traverse the file stream 
 files.forEach((file,i) => {
  //  Create an object, push Into an array 
  (self.getListData).push({
   imageUrl: '', 
  });
  
  let eachPromise = self.loadImg(file,i)
  //  Stores the current promise Object 
  promises.push(eachPromise)
 })
 
 Promise.all(promises).then(() => {
  // All done, send the request to the backend 
 }).catch(err => {
  console.log(err)
 })
},

Then let's look at loadImg. This method returns an Promise object, mainly to ensure that we get the URL of the picture and the width and height of the picture in img. onload, because these two events are asynchronous events.

In fact, the js main thread will not wait for these two results, and will continue to execute. However, because we will send Promise to resolve in img. onload, and loadAll method uses one Promise. all to wait for all promise to be completed, so that we can ensure that url and width and height of all pictures can be obtained when sending requests to the backend.


loadImg(file,i) {
 return new Promise(async (resolve,reject) => {
  let self = this
  //  Calling company wos Service, take the picture file url
  let successRes = await _fileUpload.uploadFile(item)
  if(successRes && successRes !== 'error'){
    self.getListData[i]['imageUrl'] = successRes.url
  }
  let img = new Image()
  img.src = successRes.url
  img.onload = () => {
   self.getListData[i]['width'] = img.width
   self.getListData[i]['height'] = img.height
   resolve()
  }
  img.onerror = (e) => {
   reject(e)
  }
 })
}

Let's think about it. What if we put url, which takes pictures in loadImg, into loadAll?


loadAll(files) {
 let promises = [],self = this
 
 //  Traverse the file stream 
 files.forEach(async(file,i) => {
  //  Create an object, push Into an array 
  (self.getListData).push({
   imageUrl: '', 
  });
  
  //  Calling company wos Service, take the picture file url
  let successRes = await _fileUpload.uploadFile(item)
  if(successRes && successRes !== 'error'){
    self.getListData[i]['imageUrl'] = successRes.url
  }
  
  let eachPromise = self.loadImg(file,i)
  //  Stores the current promise Object 
  promises.push(eachPromise)
 })
 
 Promise.all(promises).then(() => {
  // All done, send the request to the backend 
 }).catch(err => {
  console.log(err)
 })
},

If written this way, because the main thread execution stack of js does not wait for await to return the result, the contents after the line of await _fileUpload. uploadFile (item) in the loop will be handed over to Web APIs and then jump out of the async function. Continue to execute the main thread, and now the argument of Promise. all is an empty array, and then make a request directly. But now I don't get the URL and width and height of the picture.

The keyword await can only make async function 1 wait straight, and the execution stack can't stop and wait. After await wraps the contents behind it into Promise and gives it to Web APIs, the execution stack will jump out of async function and continue to execute until Promise finishes execution and returns results. await works only in the async function.

Summarize

From the realization of the above requirement, it seems that the understanding of event loop is deeper! Code like Promise. then and after await will wait for the return result before being put into the task queue of the corresponding event to wait for execution, and the JS thread will continue to execute down the call stack. Including watch and handler in vue, they are also put into the task queue to wait first.

Therefore, it can be seen that the event loop is very important for writing code and optimizing code in practical work ~ If you understand it incorrectly, please give more advice in the comment area.

The above is the details of re-learning the JavaScript event loop in the example. For more information about the JavaScript event loop, please pay attention to other related articles on this site!


Related articles: