【JavaScript】掌握異步編程,看這里!
當前位置:點晴教程→知識管理交流
→『 技術文檔交流 』
異步處理的概念JavaScript 中的異步處理指的是在代碼執行過程中,能夠不阻塞當前線程并處理一些時間較長的操作。異步處理通常涉及到回調函數、Promise、async/await 等機制。 在 JavaScript 中,傳統的同步處理方式采用的是阻塞式的單線程模型。這種模型的缺點是當一個任務被執行時,它會一直執行到結束,期間如果有耗時的操作也會一直阻塞下去,直到任務執行完畢,才會執行后續的任務。這種方式會導致頁面卡死,體驗非常不好。 因此,JavaScript 異步處理機制應運而生,它允許在代碼執行過程中,執行一些耗時的操作,而不會阻塞當前線程。 回調函數回調函數是一種很常見的異步編程模型,通過在異步操作完成后調用回調函數來通知異步操作已結束,從而執行后續的任務。例如: function fetchData(callback) { setTimeout(function() { const data = { name: '張三', age: 20 }; callback(data); }, 1000); } fetchData(function(data) { console.log(data); }); 在這個示例中,fetchData() 函數在完成數據加載后,調用回調函數 callback() 并傳遞數據作為參數。當數據加載完成后,控制器會跳轉到回調函數中執行后續任務。 PromisePromise 是一種比較流行的異步編程模型,它可以在異步操作完成后執行一些回調操作,并將結果返回給請求方。Promise 代表了一個異步操作的最終完成(或失敗)及其結果值。例如: function fetchData() { return new Promise(function(resolve, reject) { setTimeout(function() { const data = { name: '張三', age: 20 }; resolve(data); }, 1000); }); } fetchData().then(function(data) { console.log(data); }); 異步處理常見場景與處理策略異步處理常見場景包括但不限于:
JavaScript 引擎是單線程執行的,也就是說同一時間內只有一個任務在執行。當需要進行異步操作時,通常會使用回調函數。 假設我們有一個獲取用戶信息的異步函數 getUserInfo,在信息獲取完成后需要調用相關回調函數。一種實現方式是將回調函數作為 getUserInfo 函數的第二個參數傳入,信息獲取完成后調用該函數。 function getUserInfo(userId, callback) { setTimeout(function() { const userInfo = { id: userId, name: "Tom", age: 25 } callback(userInfo) }, 1000) } getUserInfo(1001, function(userInfo) { console.log(userInfo) }) 上述代碼首先調用 getUserInfo 函數,該函數通過 setTimeout 模擬異步操作,等待 1 秒鐘后獲取用戶信息,并在信息獲取完成后調用傳入的回調函數。最后在回調函數中輸出用戶信息。 Promise A+ 規范 Promise 的狀態 一個 Promise 的當前狀態必須為以下三種狀態中的一種:等待態(Pending)、執行態(Fulfilled)和 拒絕態(Rejected)。
一個 promise 必須提供一個 then 方法以訪問其當前值、終值和據因。 promise 的 then 方法接受兩個參數: promise.then(onFulfilled, onRejected); 其中,onFulfilled 和 onRejected 都是可選參數。
發布-訂閱模式 根據 Promise A+ 規范,每次 then 返回的值也需要滿足 thenable,也就是說我們需要將 resolve 返回值使用 promise 包裹,在本例中就是需要將返回值包裝為新的 HePromise 對象。開發之前我們不妨先來看看 Promise 鏈式調用的示例: const p = new Promise(resolve => resolve(1)); p.then(r1 => { console.log(r1); return 2; }) .then(r2 => { console.log(r2); return 3; }) .then(r3 => { console.log(r3); }); 實現all方法就是將傳入數組中的值 promise 化,然后保證每個任務都處理后,最終 resolve。示例如下: class HePromise { static all(promises: any[]) { let index = 0; const result: any[] = []; const pLen = promises.length; return new HePromise((resolve, reject) => { promises.forEach(p => { HePromise.resolve(p).then( val => { index++; result.push(val); if (index === pLen) { resolve(result); } }, err => { if (reject) reject(err); }, ); }); }); } } 編寫測試用例如下: it('HePromise.all', done => { HePromise.all([1, 2, 3]).then(res => { expect(res).toEqual([1, 2, 3]); done(); }); }); 執行測試,測試通過。 實現race方法就是將傳入數組中的值 promise 化,只要其中一個任務完成,即可 resolve。示例如下: class HePromise { static race(promises: any[]): HePromise { return new HePromise((resolve, reject) => { promises.forEach(p => { HePromise.resolve(p).then( val => { resolve(val); }, err => { if (reject) reject(err); }, ); }); }); } } 編寫測試用例: it('HePromise.race', done => { HePromise.race([11, 22, 33]).then(res => { expect(res).toBe(11); done(); }); });
整體測試代碼情況如下: async 與 await 用法及原理詳解async function test() { const res = await Promise.resolve(1) return res } 需要注意的是,使用 async、await 處理異步操作時,需要注意異常的處理。 異常處理通常我們使用 try、catch 捕獲 async、await 執行過程中拋出的異常,就像這樣: async function test() { let res = null try { const res = await Promise.resolve(1) return res } catch(e) { console.log(e) } } 從零實現一個類似 async、await 的函數 promise+generator function fn(nums) { return new Promise(resolve = >{ setTimeout(() = >{ resolve(nums * 2) }, 1000) }) } function * gen() { const num1 = yield fn(1) const num2 = yield fn(num1) const num3 = yield fn(num2) return num3 } function generatorToAsync(generatorFn) { return function() { return new Promise((resolve, reject) = >{ const g = generatorFn() const next1 = g.next() next1.value.then(res1 = >{ const next2 = g.next(res1) // 傳入上次的res1 next2.value.then(res2 = >{ const next3 = g.next(res2) // 傳入上次的res2 next3.value.then(res3 = >{ // 傳入上次的res3 resolve(g.next(res3).value) }) }) }) }) } } const asyncFn = generatorToAsync(gen) asyncFn().then(res = >console.log(res)) // 3秒后輸出 8 自動執行 自動執行其實就是運用遞歸,將生成器函數產生的數據不斷調用 next,直至執行完成。 function getData(endpoint) { return new Promise(resolve => { setTimeout(() => { resolve(`Data received from ${endpoint}`) }, 2000) }) } // 生成器函數 function* getDataAsync() { const result1 = yield getData('Endpoint 1') console.log(result1) const result2 = yield getData('Endpoint 2') console.log(result2) return 'All data received' } // 將生成器函數包裝成 Promise function asyncToPromise(generatorFn) { const generator = generatorFn() function handleResult(result) { if (result.done) { return Promise.resolve(result.value) } return Promise.resolve(result.value) .then(res => handleResult(generator.next(res))) .catch(err => handleResult(generator.throw(err))) } try { return handleResult(generator.next()) } catch (error) { return Promise.reject(error) } } asyncToPromise(getDataAsync).then(result => console.log(result))
該文章在 2024/3/29 23:46:43 編輯過 |
關鍵字查詢
相關文章
正在查詢... |