JavaScript 異步編程


博客地址:https://ainyi.com/96

眾所周知,JavaScript 是單線程的,但異步在 js 中很常見,那么簡單來介紹一下異步編程

同步編程和異步編程

同步編程,計算機一行一行按順序依次執行代碼,當前代碼任務執行時會阻塞后續代碼的執行;典型的請求-響應模型就是這樣,當請求調用一個函數或方法后,需等待其響應返回,然后執行后續代碼

異步編程,執行當前任務時(執行中),也可直接執行下一個任務;多個任務並發執行

這就涉及到兩個比較容易混淆的概念了:並行並發

並行(parallel):指同一時刻內多任務同時進行;如下圖:
755737372912ea8e89c4007.jpg


並發(concurrency):指在同一時間段內,多任務同時進行着,但是同一時刻,只有某一任務執行。使得在宏觀上具有多個進程同時執行的效果,但在微觀上只是把時間分成若干段,使多個進程快速交替地執行;如下圖:
7557373da64ffd6d1effaac.jpg

異步機制

由上面並發的解釋,可以知道單線程可以實現類似多線程機制的這種執行方式;那么 JavaScript 單線程的異步編程可以實現多任務並發執行

重點實現 js 異步的方式,就是事件循環,之前寫過關於事件循環的例子,可看:JavaScript 事件循環、異步和同步

事件循環

事件循環涉及到兩個概念:消息隊列、任務

消息隊列:也叫任務隊列,存儲待處理消息及對應的回調函數或事件處理程序
任務:js 區分同步任務和異步任務,代碼執行就是在執行任務,也就是對應同步和異步的代碼塊

首先 JavaScript 的同步任務是進入主線程的執行棧執行;異步任務則進入消息隊列(任務隊列),一個存儲着待執行任務的隊列,嚴格按照時間先后順序執行,排在隊頭的任務將會率先執行,而排在隊尾的任務會最后執行

事件循環的流程:檢查主線程執行棧是否為空,先執行執行棧中的同步任務,異步任務(回調函數)放入任務隊列中,一旦執行棧中的所有的同步任務執行完畢,就會取出任務隊列的首部壓入執行棧,開始執行,然后繼續檢查執行棧是否為空,重復這個過程

簡單來說:事件循環其實就是入棧出棧的循環

這樣就能實現異步方式

js 的異步方式

  • setTimeout
  • ajax
  • Promise
  • Generator

setTimeout
即使將時間設置為 0,也會延遲執行,即異步執行。具體可看:setTimeout 時間參數為 0 的探討

setTimeout(() => { 
   console.log('Hello!')
}, 0)

ajax

let xhr = new XMLHttpRequest()
xhr.onreadystatechange = function() { 
  if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304 ) {       
    console.log(xhr.responseText)
  } else {   
    console.log( xhr.status)
  }
}
xhr.open('GET', 'url', false)
xhr.send()

xhr.open 中第三個參數默認為 false 異步執行,改為 true 時為同步執行


Promise
promise 就經常使用了,平常使用 axios 作為請求接口的方式,就是封裝了 Promise。當然也可以自己封裝使用
具體可看:ES6 Promise 解析及詳解三個狀態

const promise = new Promise(resolve => {
  setTimeout(() => {
    resolve('hello')
  }, 1000)})
promise.then(value => {
  console.log(value, 'world')
}, error =>{
  console.log(error, 'unhappy')
})

Generator
generator 也叫做生成器,它是 ES6 中引入的一種新的函數類型,內部擁有能夠多次啟動和暫停代碼執行的強大能力,那么也能夠用於異步編程中

const axios = require('axios')

const foo = function () {
  return axios({
    method: 'GET',
    url: 'https://cosmos-alien.com/some.url'
  })
}

const main = function *() {
  try {
    let result = yield foo()
    console.log(result)
  } catch (err) {
    console.error(err)
  }
}

let it = main()
let p = it.next().value

p.then((data) => {
  it.next(data)
}, (err) => {
  it.throw(err)
})

博客地址:https://ainyi.com/96


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM