【JavaScript】掌握異步編程,看這里!
當前位置:點晴教程→知識管理交流
→『 技術(shù)文檔交流 』
異步處理的概念JavaScript 中的異步處理指的是在代碼執(zhí)行過程中,能夠不阻塞當前線程并處理一些時間較長的操作。異步處理通常涉及到回調(diào)函數(shù)、Promise、async/await 等機制。 在 JavaScript 中,傳統(tǒng)的同步處理方式采用的是阻塞式的單線程模型。這種模型的缺點是當一個任務(wù)被執(zhí)行時,它會一直執(zhí)行到結(jié)束,期間如果有耗時的操作也會一直阻塞下去,直到任務(wù)執(zhí)行完畢,才會執(zhí)行后續(xù)的任務(wù)。這種方式會導(dǎo)致頁面卡死,體驗非常不好。 因此,JavaScript 異步處理機制應(yīng)運而生,它允許在代碼執(zhí)行過程中,執(zhí)行一些耗時的操作,而不會阻塞當前線程。 回調(diào)函數(shù)回調(diào)函數(shù)是一種很常見的異步編程模型,通過在異步操作完成后調(diào)用回調(diào)函數(shù)來通知異步操作已結(jié)束,從而執(zhí)行后續(xù)的任務(wù)。例如: function fetchData(callback) { setTimeout(function() { const data = { name: '張三', age: 20 }; callback(data); }, 1000); } fetchData(function(data) { console.log(data); }); 在這個示例中,fetchData() 函數(shù)在完成數(shù)據(jù)加載后,調(diào)用回調(diào)函數(shù) callback() 并傳遞數(shù)據(jù)作為參數(shù)。當數(shù)據(jù)加載完成后,控制器會跳轉(zhuǎn)到回調(diào)函數(shù)中執(zhí)行后續(xù)任務(wù)。 PromisePromise 是一種比較流行的異步編程模型,它可以在異步操作完成后執(zhí)行一些回調(diào)操作,并將結(jié)果返回給請求方。Promise 代表了一個異步操作的最終完成(或失敗)及其結(jié)果值。例如: 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 引擎是單線程執(zhí)行的,也就是說同一時間內(nèi)只有一個任務(wù)在執(zhí)行。當需要進行異步操作時,通常會使用回調(diào)函數(shù)。 假設(shè)我們有一個獲取用戶信息的異步函數(shù) getUserInfo,在信息獲取完成后需要調(diào)用相關(guān)回調(diào)函數(shù)。一種實現(xiàn)方式是將回調(diào)函數(shù)作為 getUserInfo 函數(shù)的第二個參數(shù)傳入,信息獲取完成后調(diào)用該函數(shù)。 function getUserInfo(userId, callback) { setTimeout(function() { const userInfo = { id: userId, name: "Tom", age: 25 } callback(userInfo) }, 1000) } getUserInfo(1001, function(userInfo) { console.log(userInfo) }) 上述代碼首先調(diào)用 getUserInfo 函數(shù),該函數(shù)通過 setTimeout 模擬異步操作,等待 1 秒鐘后獲取用戶信息,并在信息獲取完成后調(diào)用傳入的回調(diào)函數(shù)。最后在回調(diào)函數(shù)中輸出用戶信息。 Promise A+ 規(guī)范 Promise 的狀態(tài) 一個 Promise 的當前狀態(tài)必須為以下三種狀態(tài)中的一種:等待態(tài)(Pending)、執(zhí)行態(tài)(Fulfilled)和 拒絕態(tài)(Rejected)。
一個 promise 必須提供一個 then 方法以訪問其當前值、終值和據(jù)因。 promise 的 then 方法接受兩個參數(shù): promise.then(onFulfilled, onRejected); 其中,onFulfilled 和 onRejected 都是可選參數(shù)。
發(fā)布-訂閱模式 根據(jù) Promise A+ 規(guī)范,每次 then 返回的值也需要滿足 thenable,也就是說我們需要將 resolve 返回值使用 promise 包裹,在本例中就是需要將返回值包裝為新的 HePromise 對象。開發(fā)之前我們不妨先來看看 Promise 鏈式調(diào)用的示例: 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); }); 實現(xiàn)all方法就是將傳入數(shù)組中的值 promise 化,然后保證每個任務(wù)都處理后,最終 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(); }); }); 執(zhí)行測試,測試通過。 實現(xiàn)race方法就是將傳入數(shù)組中的值 promise 化,只要其中一個任務(wù)完成,即可 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 執(zhí)行過程中拋出的異常,就像這樣: async function test() { let res = null try { const res = await Promise.resolve(1) return res } catch(e) { console.log(e) } } 從零實現(xiàn)一個類似 async、await 的函數(shù) 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 自動執(zhí)行 自動執(zhí)行其實就是運用遞歸,將生成器函數(shù)產(chǎn)生的數(shù)據(jù)不斷調(diào)用 next,直至執(zhí)行完成。 function getData(endpoint) { return new Promise(resolve => { setTimeout(() => { resolve(`Data received from ${endpoint}`) }, 2000) }) } // 生成器函數(shù) function* getDataAsync() { const result1 = yield getData('Endpoint 1') console.log(result1) const result2 = yield getData('Endpoint 2') console.log(result2) return 'All data received' } // 將生成器函數(shù)包裝成 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 編輯過 |
關(guān)鍵字查詢
相關(guān)文章
正在查詢... |