JavaScript 非同步編程
- 2020 年 9 月 15 日
- 筆記
- javascript
部落格地址://ainyi.com/96
眾所周知,JavaScript 是單執行緒的,但非同步在 js 中很常見,那麼簡單來介紹一下非同步編程
同步編程和非同步編程
同步編程,電腦一行一行按順序依次執行程式碼,當前程式碼任務執行時會阻塞後續程式碼的執行;典型的請求-響應模型就是這樣,當請求調用一個函數或方法後,需等待其響應返回,然後執行後續程式碼
非同步編程,執行當前任務時(執行中),也可直接執行下一個任務;多個任務並發執行
這就涉及到兩個比較容易混淆的概念了:並行 和 並發
並行(parallel):指同一時刻內多任務同時進行;如下圖:
並發(concurrency):指在同一時間段內,多任務同時進行著,但是同一時刻,只有某一任務執行。使得在宏觀上具有多個進程同時執行的效果,但在微觀上只是把時間分成若干段,使多個進程快速交替地執行;如下圖:
非同步機制
由上面並發的解釋,可以知道單執行緒可以實現類似多執行緒機制的這種執行方式;那麼 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: '//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)
})
部落格地址://ainyi.com/96