Evolution of JavaScript asynchronous operation

Time:2021-7-29

Special note

  • This blog is minepersonalyesJavaScript asynchronous operationYesSummary and classification
  • Through this article, I also hope that readers can learn frommacroscopicLook at how JavaScript asynchronous operations evolve.
  • But if you want to master it all through this blogpromiseperhapsasyncAll the knowledge of functions and other technologies is still unrealistic.
  • We recommend intensive readingRuan YifengTeacher’sECMAScript 6 getting started – promise object, andNicholasTeacher’sDeep understanding of ES6Chapter 11 of (page 219).

abstract

In a philosophical way: the ultimate goal of JavaScript asynchronous operation evolution is to make asynchronous code 
Looks more like synchronized code.
  The nature of single thread operation of JS language does not make people who use it feel that it is chicken ribs, but makes programs 
Researchers have created a variety of tools to improve its performance.
  From callback function to promise object, to async function considered as the final solution of JS asynchronous operation.
Every evolution is from nothing, from community to standard.
  This blog will start from the source and first explore why JS needs asynchronous operation. Then explain some conceptual problems
Noun. Finally, the development process of JS asynchronous operation is reviewed.

key word

Synchronous | Asynchronous | Event Loop | CallBack | Promise | Generator | Async/Await

Why does JavaScript require asynchronous operations

Single thread

  • JavaScript is designed in this languageoriginal intentionForSolve the problem of interaction between users and browsers
  • One of the highlights isDOM operation。 Imagine that a code block is inModify DOMIn addition, there is a code block that needs to beDelete DOM。 So whose advice should I listen to?
  • In order to avoid complex thread synchronization problems, there is only one thread responsible for executing code in JS execution environment.This is what we often call JavaScript single thread working mode.

Working mode

  • Sometimes I’m doing somethingIt takes timeYou need towait forThe next task cannot be entered until the current task is executed. Then the program will appearFalse death, which is what we often sayblock
  • To avoid this, JS willWorking modeIt is mainly divided into two categories:Synchronous modeandAsynchronous mode

Some concepts

Synchronous

The code executed in synchronous mode will beExecution stackQueue execution in. That’s what we often sayStack pressing operation, it will be deleted after runningPop stackClosure functionYou can put a variablePersistent preservationIn the execution stack.

Asynchronous

Asynchronous mode codeWill not enter the main thread, which is our execution stack. But intoTask queueOrMessage queueYes. WhenExecution stackAll inSynchronization taskAfter execution, the system will read itTask queue, thoseAsynchronous codeWill end the wait,Enter execution stack, start execution.

NotesynchronizationstillasynchronousMeansOperating environmentProvidedAPISosynchronizationorasynchronousMode.

Synchronization API: console. Log ()
Asynchronous API: settimeout()

Stack

Execution stack. When the main thread runs, it generates heap and stack. The code in the stack calls various external APIs, which add various events to the task queue.

Message queue

Message queuing.

Web API

Various API interfaces provided by the browser.

Event loop

as long asStackThe code in is executed,Main threadWill be fromMessage queueRead asynchronous operation in. This process is cyclic, so the whole operation mechanism is also calledevent loopEvent Loop

Refer to the following figure for the relationship between stack, message queue, event loop and web API:

Evolution of JavaScript asynchronous operation

Evolution of JavaScript asynchronous operation

history of evolution

CallBack

Callback functionIs the earliest implementation of asynchronous operation. It is made byCaller definition, give it toExecutor executionFunction of. Several common applications are:Event mechanismandPublish subscribe modeWait.

var xhr = new XMLHttpRequest();
xhr.open('GET', url);
Xhr.onload = function() {...} // the callback function triggered when the complete response data is received
Xhr.onerror = function() {...} // the callback function triggered when sending an error
xhr.send()

defect: when we need to sendMultiple requestsAnd in these requestsAll return successWhen dealing with all the request results, we have to think of some special skills. The easiest way to do this is to make every requestnestingGet up and execute the next request when one request succeeds. First of all, it will be a waste of time, and then it will form what we often sayCallback hellThe case makes the code bothUnsightlyalsoHard to maintain

/*First floor*/
$.ajax({
    url: URL1,
    /* callback1 */
    success: function () {
        /*Second floor*/
        $.ajax({
            url: URL2,
            /* callback2 */
            success: function () {
                ...
                /*Layer n*/
                $.ajax({ ... })
            }
        })
    }
})

Promise

Promise is aobject, forAsynchronous operationYesresultPreparedplaceholder 。 Used to representAsynchronous taskAfter thatsuccessstillfail。 PromisestateOnce it’s determined, we’llCannot be modified

  • Promise life cycle: in executionAsynchronous operationWhen, willpromiseGiving a result before giving the final result is calledpendingStatus, there are two results, successfulfulfilledStatus and failedrejectedStatus. After the results are given, some response (account) needs to be madetask), corresponding toonFulfilledandonRejected

Evolution of JavaScript asynchronous operation

  • Then() method: you can use the then () method in promise’sWhen the state changesPerform some specific operations.

    Promise’s essence is to useCallback functiondefinitionAsynchronous taskAfter the completion of the required tasks.

    function ajax (url) {
      return new Promise(function (resolve, reject) {
          var xhr = new XMLHttpRequest()
          xhr.responseType = 'json'
          xhr.onload = function () {
              if (this.status === 200) {
                  resolve(this.response)
              } else {
                  reject(new Error(this.statusText))
              }
          }
          xhr.send()
      })
    }
    
    ajax('/api/user.json').then(function (res) {
      console.log(res)
    }, function (error) {
      console.log(error)
    })
  • Tandem promise

    • The then () method of the promise object returns aNew promise object
    • The latter then () method is the promise returned for the previous thenRegistering Callbacks
    • The callback function in the previous then () methodReturn valueIt will be used as a callback for the then () methodparameter
    • If in callbackPromise is returned, the callback of the then () method will wait for its end
    • Therefore, promise can be carried outcall chaining Yes. Each then () method is actually a promise object returned for the previous then () methodAdd callback after status is clear
    let p1 = new Promise(function(resolve, reject) {
      resolve(42);
    })
    
    p1.then(function (value) {
      console.log(value)
    }).then(function () {
      console.log("Finished")
    })
    
    // 42
    // Finished
  • Capture error

    • Onrejected callbackIn promisefailOr appearabnormalWill beimplement
    • The catch () method is equivalent to the second parameter accepted by the then () method, but the difference is–The catch () method in the promise chain allows us to catch errors in the completion or rejection handler of the previous promise.
    let p1 = new Promise(function(resolve, reject) {
      throw new Error("Explosion")
    })
    
    p1.catch(function (error) {
      console.log(error.message)
      throw new Error("Boom")
    }).catch(function (error) {
      console.log(error.message)
    })
  • Promise static method

    /* Promise.resolve() */
    let promise = Promise.resolve(42)
    
    promise.then(function (value) {
      console.log(value)  // 42
    })
    
    /* Promise.reject() */
    let promise = Promise.reject(42)
    
    promise.catch(function (value) {
      console.log(value)  // 42
    })
    /*The following two expressions are equivalent*/
    Promise.resolve('foo')
    .then(function (value) {
      console.log(value)
    })
    
    new Promise(function (resolve, reject) {
      resolve('foo')
    })
  • Promise parallel execution

    /* Promise.all()
     *This method takes a single iteratable object (such as an array) as a parameter and returns a promise. 
     *After all the iteratable promise elements are completed, the returned promise will be completed.
     */
    let p1 = new Promise(function (resolve, reject) {
      resolve(42)
    })
    
    let p2 = new Promise(function (resolve, reject) {
      reject(43)
    })
    
    let p = Promise.all([p1, p2])
    
    p.catch(function (value) {
      console.log(value)  // 43
    })
    /* Promise.race()
     *The method also receives a promise iteratable object and returns a new promise.
     *Once one of the source promises is resolved, the returned promise will be resolved immediately.
     */
     let p1 = Promise.resolve(42)
     
     let p2 = new Promise(function (resolve, reject) {
       resolve(43)
     })
     
     let p = Promise.race([p1, p2])
     
     p.then(function (value) {
       console.log(value)  // 42
     })

Macro and micro tasks

Callback queueThe tasks in are calledMacro task。 During the execution of macro tasks, some additional requirements can be added temporarily. You can choose as oneNew macro taskEnter the task queue to queue up, or asCurrent taskYesMicro taskExecute directly after the end of the current taskMicro tasksureImprove overall responsiveness, the promise callback will be executed as a micro task. have access tosetTimeOut()add toMacro task

console.log("global start")

setTimeOut(() => {
    console.log("setTimeOut")
}, 0)

Promise.resolve()
.then(() => {
    console.log("Promise")
})
.then(() => {
    console.log("Promise2")
})

console.log("global end")

// global start
// global end
// Promise
// Promise2
// setTimeOut

Generator

Generator execution procedure:

  • When defining, there is a before the function name*number
  • When you call the generator function, you do not immediately execute the function, but you get aGenerator object
  • When we call thisGenerator objectYesnext()I’ll go when I canimplement
  • Will continue untilyieldThe location of the keyword, andValue after yield returnGo out, and the function will pause.Yield return valueWill be received in the form of{ value: "foo", done: false }
  • When weCall againThe next () method, andIncoming parameters, then the function will continue to execute, and the parameters we pass in will beReturn value of yield
  • If we call the generator object outsidethrow()Method, the function will get the exception. Can be used inside functionstry...catch...The wayCatch exception

    function * main () {
      const users = yield ajax('/api/users.json')
      console.log(users)
      
      const posts = yield ajax('/api/posts.json')
      console.log(posts)
    }
    
    const g = main()
    
    const result = g.next()
    
    result.value.then(data => {
      const result2 = g.next(data)
      if (result2.done) return
      
      result2.value.then(data => {
          ...
      })
    })

Async/Await

  • Execute async function and return promise objects

    async function test1 () {
      return 1
    }
    
    async function test2 () {
      return Promise.resolve(2)
    }
    
    const result1 = test1()
    const result2 = test2()
    
    console.log('result1', result1)
    console.log('result1', result1)
  • Promise. Then() is successful, corresponding to await

    async function test3 () {
      const p3 = Promise.resolve(3)
      p3.then(data => {
          console.log('data', data)
      })
      
      const data = await p3
      console.log('data', data)
    }
    async function test4 () {
      const data4 = await 4
      console.log('data4', data4)
    }
    
    async function test5 () {
      const test5 = await test1()
      console.log('test5', test5)
    }
  • Promise. Catch() exception, corresponding to try... Catch

    Sometimes we want to not interrupt the following (asynchronous) operation even if the previous (asynchronous) operation fails. Then you can usetry...catch...To catch exceptions

    async function test6 () {
      const p6 = Promise.reject(6)
      try {
         const data6 = await p6
         console.log('data6', data6)
      } catch (e) {
          console.log('e', e)
      }
    }

Acknowledge

  • Thanks to every programmer who contributes to JavaScript. I also thank every “Lurk” who is making efforts and look forward to your outbreak.
  • Miss Ruan Yifeng
  • Mr Nicholas
  • Station B “it course winner”