JavaScript 異步處理

Posted by Adam on August 24, 2022
# Promise ### 實例方法(Instance Methods:鏈式調用) ```javascript // 模擬一個非同步任務 const fetchData = () => new Promise((resolve, reject) => { Math.random() > 0.5 ? resolve('🎉 成功資料') : reject('❌ 連線失敗'); }); // 實例方法展示:利用流式寫法(Method Chaining)接龍 fetchData() // 1. .then() -> 接收成功結果,可回傳新值傳給下一個 .then .then(result => { console.log(`[.then 1]: ${result}`); return '轉換後的資料'; }) // 2. .then() 連點 -> 接收上一個 .then 回傳的內容 .then(transformedData => console.log(`[.then 2]: ${transformedData}`)) // 3. .catch() -> 救援大隊。前面任何一個步驟發生錯誤(Reject),都會直接跳到這裡 .catch(error => console.error(`[.catch 捕捉錯誤]: ${error}`)) // 4. .finally() -> 終點站。無論最後是成功還是失敗,都必定會執行(常用於關閉 Loading 動畫) .finally(() => console.log('[.finally]: 流程結束,清理資源')); ``` --- ### 靜態方法(Static Methods:多工管理) ```javascript // 準備三個不同時間、不同結果的 Promise 任務 const taskA = new Promise(resolve => setTimeout(() => resolve('A成功'), 1000)); const taskB = new Promise((resolve, reject) => setTimeout(() => reject('B失敗'), 500)); const taskC = new Promise(resolve => setTimeout(() => resolve('C成功'), 2000)); // ===================================================================== // 1. Promise.all() ->「一人陣亡,全隊失敗」 // 必須全部成功才算成功;只要有一個人失敗,就立刻集體判出局進入 catch。 // ===================================================================== Promise.all([taskA, taskB, taskC]) .then(results => console.log('all 全部成功:', results)) .catch(err => console.log('all 失敗(因為B失敗了):', err)); // 👈 本範例會執行這行 // ===================================================================== // 2. Promise.allSettled() ->「不論死活,通通回報」 // 不管成功或失敗,會等待所有人執行完畢,並詳細列出每個任務的最終狀態。 // ===================================================================== Promise.allSettled([taskA, taskB, taskC]) .then(results => { console.log('allSettled 所有人最終狀態:', results); /* 輸出結構: [ { status: 'fulfilled', value: 'A成功' }, { status: 'rejected', reason: 'B失敗' }, { status: 'fulfilled', value: 'C成功' } ] */ }); // ===================================================================== // 3. Promise.race() ->「速度爭霸戰」 // 看誰跑得最快!第一個抵達終點的人不管是成功還是失敗,就以他的結果為準。 // ===================================================================== Promise.race([taskA, taskB, taskC]) .then(res => console.log('race 贏家:', res)) .catch(err => console.log('race 最快抵達的是失敗:', err)); // 👈 本範例會執行這行(B僅需0.5秒最快) // ===================================================================== // 4. Promise.any() ->「尋找第一道曙光」 // 只要有任何一個成功就立刻宣告勝利;必須「全部人都失敗」,才算真正失敗進入 catch。 // ===================================================================== Promise.any([taskA, taskB, taskC]) .then(res => console.log('any 第一個成功的是:', res)) // 👈 本範例會執行這行(A是第一個成功的) .catch(err => console.log('any 全部都失敗才會看到我:', err)); // ===================================================================== // 5. 快速建立狀態的捷徑 // ===================================================================== const immediateSuccess = Promise.resolve('直接成功'); // 建立一個狀態已經是 fulfilled 的 Promise const immediateFailure = Promise.reject('直接失敗'); // 建立一個狀態已經是 rejected 的 Promise ``` # async / await 在 JavaScript 中,async 函數是一種用來處理異步操作的函數。當一個函數被聲明為 async 時,該函數內部的代碼會被當做 Promise 對象執行,並且可以使用 await 關鍵字來等待異步操作的完成。 簡單來說,async 函數允許你以同步的方式編寫異步操作的程式碼,使得程式碼更易讀且更易維護。當調用 async 函數時,該函數將返回一個 Promise 對象,可以使用 then 方法來處理成功的結果,也可以使用 catch 方法來處理錯誤的情況。 以下是一個示例,展示了如何定義一個 async 函數並使用 await 關鍵字來等待異步操作的完成: ```javascript // 1. 使用 async 關鍵字定義函式,標記這是一個「非同步隔離區」 // 這保證了 fetchData 執行後「必定會回傳一個 Promise 物件」 async function fetchData(url) { // 2. await 會暫停函式內部的執行,等待 fetch(url) 這個非同步網路請求完成 // 此時主執行緒會直接跳出此函式去忙別的事(例如網頁渲染),完全不卡死網頁 // 當請求成功後,會將回傳的 Response 物件賦值給 response 變數 const response = await fetch(url); // 3. 再次使用 await 暫停,因為將資料解析成 JSON 格式也是一個非同步操作(回傳 Promise) // 解析完成後,將真正的資料(物件或陣列)賦值給 data 變數 const data = await response.json(); // 4. 回傳解析後的資料。雖然這裡看似回傳普通變數,但因為在外層有 async // JavaScript 會自動將它包裝成 Promise.resolve(data) 送出去 return data; } // 5. 呼叫非同步函式,此時它會立刻回傳一個 Promise 物件 fetchData('https://api.example.com/data') // 6. .then() 是 Promise 的成功接班人 // 當函式內部的 return data 成功完成時,會觸發這裡,並將 data 印出來 .then(data => console.log(data)) // 7. .catch() 是 Promise 的錯誤救援大隊 // 不論是網路斷線(fetch 失敗)還是解析失敗(json 失敗),只要中途出事, // 流水線就會立刻中斷,並直接跳到這裡印出錯誤訊息 .catch(error => console.error(error)); ```