Asynchronous programming mechanism in JS

Currently, we have three tasks:

  1. Console. log(” Sync operation “)
  2. Loading pictures
  3. Timer execution

The above three tasks have two execution modes: single thread and multi-thread. Since JS is ** single threaded, ** we will only use single thread to demonstrate.

A single thread can only complete one task at a time and wait for the first task to complete before it can execute the next one, thus causing blocking and affecting the execution efficiency of subsequent tasks. Therefore, the main thread of JS will produce another mode of execution for asynchronous tasks.

  1. Identify all tasks to be completed, assign synchronous tasks to the main thread for execution, and assign asynchronous tasks to specific execution modules, such as recording images to specific HTTP network request modules

  2. The main thread performs synchronization tasks, and the module is thrown to the task list after completing asynchronous tasks

  3. After the main thread completes all tasks in the main thread list, it continuously polls the tasks in the main thread list. After polling, the tasks are assigned to the main thread for further execution. After executing the tasks, it continuously polls the tasks because the completion time of asynchronous tasks is uncertain.

Asynchronously load image JS task operation

let imgSrc = 'https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimage.bitauto.com%2Fdealer%2Fnews%2F100136406%2F43570831-271b-418 7- bacc-c052974f644a.jpg&refer=http%3a%2f%2fimage.bitauto.com & app = 2002 & size = f9999, 10000 & q = a80 & n = 0 & g = 0 n & FMT = jpeg? The SEC = 16231 53161&t=1b24914df265e0339c053361ec9a3d7c' function loadImg(src,resolve,reject){ ket img = new Image() img.src = src Img. onload = ()=>{resolve(img)} img. onError = ()=>{reject} // loadImg(SRC, (img) = > {document. The body. The appendChild (img)}, () = > {the console. The log (' failure to load ')}) / / synchronization console, log (" synchronization task ")Copy the code

The actual execution is actually performed by the callback function. When console.log completes execution in the main thread, it polls the task list to see if there is any completed operation, and when onLoad loads the image, it is thrown into the task list for execution by the main thread

Timer task polling

As mentioned above, after the module completes the asynchronous operation, it will throw the callback to the task list and the main thread will find and execute it, so we use setInterval to continuously add tasks to the task list, and the main thread will continuously execute tasks

function inter(callback,delay=100){
    let id = setInterval(()=>{
        callback(id)
    })
}

inter(id => {
    let div = document.querySelector('div')
    let left = parseInt(window.getComputedStyle(div).left)
    div.style.left = `${left+10}px`
    if(left>=300){ 
        clearInterval(id)
        
        inter(id=>{
            let width = parseInt(div.style.width)            
            if(width <= 20){ clearInterval(id) }
            div.style.width = `${width-10}px`
        }) 
    }
})
Copy the code

Timer module constantly add tasks to the task list, the main thread after performing synchronization tasks, constantly polling task list to complete the task added by the module, with the above code can be seen intuitively.

Use file dependencies to understand task alignment

It is known from the above that an asynchronous task will be assigned to a specific functional module for execution and then added to the task list after execution.

But in this case, file A depends on the functions of file B to execute, and the task list is first in first out,

Therefore, callback nesting is needed to prioritize module processing. The next asynchronous task is triggered only after the current asynchronous task is completed

function load(src,callback){ let script = document.createElement('script') script.src = src script.onload = ()=>{ resolve }; Document. The body. The appendchild (script)} the load ('. / a. s' () = > {a () the load ('. / b.j s' () = > {b () / / is dependent on a () method of})})Copy the code

This nesting approach solves the task sequencing problem very well, and the second task is executed only after the first asynchronous execution is complete.

But this asynchronous solution can lead to more and more nested callbacks, leading to callback hell.

The callback hell

Let’s take a visual look at the way callback hell is written to highlight the advantages of our subsequent asynchronous solution

Ajax (' request address, (res) = > {ajax (' request address, (res1) = > {ajax (' request address, (res2) = > {ajaxx (' request address, (res2) = > {})})})})Copy the code

This layer upon layer of requests leads to very poor readability and maintainability of our code.

So this callback is backward writing