目錄
1.同步和異步的區別
單線程和異步
1.JS是單線程語言,只能同時做一件事
2.瀏覽器和nodejs已支持JS啓動進程,比如Web Worker
3.JS和DOM渲染共用同一個線程,因爲JS可修改DOM結構
DOM渲染時,JS執行必須停止,JS執行時,DOM渲染也必須停止
爲什麼需要異步
遇到等待(網絡請求,定時任務)不能卡住,總不能去等待網絡請求然後瀏覽器卡在這裏吧????
等待的過程cpu也是空閒的,這樣也會浪費資源
異步回調都是用callback的函數形式
異步:
同步:
不點擊alert就會阻塞卡住,3不會打印
異步不會阻塞代碼執行,同步會阻塞代碼執行
2.應用場景
異步主要用在等待的情況(網絡請求、定時任務)
先打印start,再異步執行網絡請求,接着打印end,網絡請求什麼時候回來就什麼時候打印data1的數據
先打印start,接着定義image,再加上onload事件,什麼時候加載完就什麼時候執行回調,這也是回調的另一種形式。
接着執行img.src賦值,這裏一執行就開始加載圖片,然後打印end,最後圖片加載完的時候執行回調打印loaded
3.promise
早期JS會陷入回調地獄(callback hell),獲取第一份數據之後再獲取第二份數據,獲取到第二份數據後再獲取第三份數據...這樣的嵌套回調太多了就特別亂,就像地獄一般
嵌套的回調地獄的問題導致了promise的出現,也就是說promise的出現,解決了callback嵌套的回調地獄(callback hell)問題
promise的then都是並列的管道形式,當然這裏也是回調,但是不是嵌套的更方便調試和查看
具體promise的例子我們後面在說,這裏看一下形式
4.練習題
1.手寫用promise加載一張圖片
function loadImg(src) {
const p = new Promise(
(resolve, reject) => {
const img = document.createElement('img')
img.onload = () => {
resolve(img)
}
img.onerror = () => {
const err = new Error(`圖片加載失敗 ${src}`)
reject(err)
}
img.src = src
}
)
return p
}
// const url = 'https://img.mukewang.com/5a9fc8070001a82402060220-140-140.jpg'
// loadImg(url).then(img => {
// console.log(img.width)
// return img
// }).then(img => {
// console.log(img.height)
// }).catch(ex => console.error(ex))
const url1 = 'https://img.mukewang.com/5a9fc8070001a82402060220-140-140.jpg'
const url2 = 'https://img3.mukewang.com/5a9fc8070001a82402060220-100-100.jpg'
loadImg(url1).then(img1 => {
console.log(img1)
return img1 // 普通對象
}).then(img1 => {
console.log(img1)
return loadImg(url2) // promise 實例
}).then(img2 => {
console.log(img2)
return img2
}).then(img2 => {
console.log(img2)
}).catch(ex => console.error(ex))
運行結果:
深入細節:當一個 Promise 完成(fulfilled)或者失敗(rejected)時,返回函數將被異步調用(由當前的線程循環來調度完成)。具體的返回值依據以下規則返回。如果 then 中的回調函數:
①返回了一個值,那麼 then 返回的 Promise 將會成爲接受狀態,並且將返回的值作爲接受狀態的回調函數的參數值。
執行then之後是會返回Promise對象的,比如這裏回調函數返回img1,然後將返回值img1作爲參數傳給下一個then的參數,這裏PromiseValue是img,因爲loadImg(url1)會執行resolve(img),resolve函數返回promise對象,這個img就是上面的img1,這是第一個url1加載出來的img
②沒有返回任何值,那麼 then 返回的 Promise 將會成爲接受狀態,並且該接受狀態的回調函數的參數值爲 undefined。
比如下圖,沒有返回值的Promise對象的PromiseValue就是undefined
③拋出一個錯誤,那麼 then 返回的 Promise 將會成爲拒絕狀態,並且將拋出的錯誤作爲拒絕狀態的回調函數的參數值。
這裏的③和⑤我準備一起說,所以要看例子請直接看到⑤的例子
④返回一個已經是接受狀態的 Promise,那麼 then 返回的 Promise 也會成爲接受狀態,並且將那個 Promise 的接受狀態的回調函數的參數值作爲該被返回的Promise的接受狀態回調函數的參數值。
這裏因爲返回接受狀態的promise,所以傳給下一個then就是img,因爲執行了resolve(img),所以resolve函數返回的promise對象包含img,這是第二個url2加載出來的img,最後打印<img>標籤也和上面2個不同
⑤返回一個已經是拒絕狀態的 Promise,那麼 then 返回的 Promise 也會成爲拒絕狀態,並且將那個 Promise 的拒絕狀態的回調函數的參數值作爲該被返回的Promise的拒絕狀態回調函數的參數值。
請求失敗的情況,並且最後沒有catch的情況下,PromiseStatus爲rejected
這裏到then裏面的回調函數return loadImg(....)的時候就是返回已經是拒絕狀態的Promise,即執行了reject(err)返回的Promise,那麼then返回的Promise也是拒絕狀態,往後傳參數的的時候,傳的err,下一個then的參數img2就是err(就是③中的將拋出的錯誤作爲拒絕狀態Promise的回調函數的參數值),所以這裏PromiseValue是Error: 圖片加載失敗 https://img3.mukewang123123.com/5a9fc8070001a82402060220-100-100.jpg at HTMLImageElement.img.onerror (<anonymous>:9:29)
這裏返回的Promise的狀態居然是resolved,error都打印了爲什麼還是resolved呢?因爲我們catch了,捕捉了錯誤,所以狀態執行正確,沒問題,如果去掉catch,那麼最後的拒絕狀態的Promise沒有被捕捉處理,PromiseStatus就是rejected
⑥返回一個未定狀態(pending)的 Promise,那麼 then 返回 Promise 的狀態也是未定的,並且它的終態與那個 Promise 的終態相同;同時,它變爲終態時調用的回調函數參數與那個 Promise 變爲終態時的回調函數的參數是相同的。
第一次請求不到則超時,PromiseStatus爲pending,最後加不加catch只是影響能不能捕捉到拒絕狀態Promise的問題,這裏不會影響Promise的pending狀態
2.setTimeout筆試題
運行結果
先打印1354,1s後打印2
更復雜的題目Promise和setTimeout混合的涉及到事件循環,大家可以看我另一篇博客:
關注、留言,我們一起學習。
===============Talk is cheap, show me the code================