該系列下的前幾篇文章分別對不一樣的幾種異步方案原理進行解析,本文將介紹一些實際場景和一些常見的面試題。(積累不太夠,後面想到再補)面試
流程調度,最多見的就是繼發和併發(或者說串行和並行)兩種類型,在平常工做裏都很常見。接下來結合實際場景進行說明:segmentfault
串行執行的關鍵是,將每個異步任務放到前一個異步任務的回調函數裏執行。promise
// 這裏假定一共要執行5個動畫 // getAnimation 函數模擬執行動畫 接收參數i表述動畫編號 返回一個promose const getAnimation = (i) => new Promise((resolve, reject) => { setTimeout(()=>{ // 隨機返回true或者false const isSuccess = Math.random() > 0.5 console.log(`第${i}個動畫執行`) if(isSuccess){ return resolve(isSuccess) } return reject(isSuccess) },1000) }) // 1.promise實現 核心就是嵌套代碼 const serialScheduleByPromise = () => { let p = Promise.resolve(true) const tasks = [] for(let i=0;i < 5; i++){ p = p.then(isSuccess=>{ if(isSuccess){ return getAnimation(i+1) } }).catch((err)=>{ return console.log(`執行失敗`) }) } } serialScheduleByPromise() // 2.async/await實現 const serialScheduleByAsync = async () => { try{ for(let i=0;i < 5; i++){ await getAnimation(i+1) }}catch(e){ console.log(`執行失敗`) } } serialScheduleByAsync()
async/await
的語法雖然沒有單獨解析,可是本質就是前一篇介紹的帶自動執行器的generator
而已,所以再也不贅述
能夠看到,async的寫法代碼更簡潔,並且邏輯更清晰,可讀性更強。瀏覽器
場景:併發讀取5個數據(爲了方便 分別編號爲1-5),而後按照實際讀取順序結果併發
const getDataById = (i) => new Promise((resolve, reject) => { // 隨機延遲一個時間返回結果, const delay = Math.floor(Math.random() * Math.floor(3000)) // 延遲時間可能爲 0,1000,2000 毫秒 setTimeout(()=>{ return resolve(i) }, delay) }) // 1.promise實現 const concurrentScheduleByPromise = ()=>{ const promises = [] const result = [] for(let i = 0;i < 5;i++){ promises[i] = getDataById(i+1) promises[i].then(i=>{ result.push(i) }) } Promise.all(promises).then(()=>{ result.forEach(id=>{ console.log(id) }) }) } concurrentScheduleByPromise() // async/await實現 const concurrentScheduleByAsync = () => { for(let i = 0 ;i < 5; i++){ let task = async function (){ console.log(await getDataById(i+1)) } task() } } concurrentScheduleByAsync()
注意辨析這裏concurrentScheduleByAsync
和serialScheduleByAsync
的區別,關鍵點是同一個async
函數內部的await
纔是按順序執行。dom
流程調度裏比較常見的一種錯誤是「看似串行」的寫法,能夠感覺一下這個例子:異步
const getPromise = (name) =>new Promise(resolve=>{ setTimeout(()=>{ console.log(name) resolve(name) },1000) }) // 判斷如下幾種寫法的輸出結果 Promise.resolve().then(getPromise('1a')).then(getPromise('1b')).then(getPromise('1c')) Promise.resolve().then(()=>getPromise('2a')).then(()=>getPromise('2b')).then(()=>getPromise('2c')) Promise.resolve().then(getPromise('3a').then(getPromise('3b').then(getPromise('3c')))) Promise.resolve().then(()=>getPromise('4a').then(()=>getPromise('4b').then(()=>getPromise('4c'))))
這類題目通常出如今面試題裏。async
console.log(1) new Promise(resolve => { console.log(2) setTimeout(() => { console.log(10) }, 10) resolve() console.log(3) }).then(() => { console.log(5) }) setTimeout(() => { console.log(7) Promise.resolve().then(() => { console.log(9) }) console.log(8) }) Promise.resolve().then(() => { console.log(6) }) console.log(4) // 輸出 1 2 3 4 5 6 7 8 9 10
<style> .outer { padding: 30px; background-color: aqua; } .inner { height: 100px; background-color: brown; } </style> <body> <div class="outer">outer <div class="inner">inner</div> </div> </body> <script> var outer = document.querySelector('.outer'); var inner = document.querySelector('.inner'); // Let's listen for attribute changes on the // outer element new MutationObserver(function () { console.log('mutate'); }).observe(outer, { attributes: true }); // Here's a click listener… function onClick() { console.log('click'); setTimeout(function () { console.log('timeout'); }, 0); Promise.resolve().then(function () { console.log('promise'); }); outer.setAttribute('data-random', Math.random()); } // …which we'll attach to both elements inner.addEventListener('click', onClick); outer.addEventListener('click', onClick); // innner.click() 試試直接點擊和js執行click的區別 </script>
這類問題實質上就是辨析異步任務隊列類型,詳細內容和解析能夠直接看js異步從入門到放棄(三)- 異步任務隊列(task queues)。函數
這篇文章主要是給這個系列作個簡單的收尾,單獨純異步的問題難點其實也很少,偷個懶,後面想到了再補上。動畫
若是以爲寫得很差/有錯誤/表述不明確,都歡迎指出
若是有幫助,歡迎點贊和收藏,轉載請徵得贊成後著明出處。若是有問題也歡迎私信交流,主頁有郵箱地址
若是以爲做者很辛苦,也歡迎打賞~