摘要:前言業務開發中經常會用到異步函數,這里簡單的對異步函數以及它的各種各樣的解決方案做一個淺析優缺點優點能夠極大的提高程序并發業務邏輯的能力缺點異步函數的書寫方式和代碼執行邏輯很不直觀,回調函數這種方式不太符合人類的的線性思維異步函數的執行流程
前言
優缺點:業務開發中經常會用到異步函數,這里簡單的對異步函數以及它的各種各樣的解決方案做一個淺析
優點:
能夠極大的提高程序并發業務邏輯的能力.
缺點:
異步函數的書寫方式和代碼執行邏輯很不直觀,回調函數這種方式不太符合人類的的線性思維
異步函數的執行流程通常不好管理
不好對異步函數部署錯誤處理機制
解決方案針對異步函數存在的缺點,所以才有了形形色色的異步的處理方案,常見的比如
原生的回調函數
promise/A+
async/await(generator);
業務場景但這些解決方案各自能解決什么問題,才是我們所關心的.
實際上,如果對業務場景進行抽象,開發過程中對異步函數的管理可以抽象成如下的幾種需求
比如有異步函數f1,f2,f3:
對f1,f2,f3之間的執行順序沒有要求. 它們的執行結果不互相依賴,誰先完成誰后完成無關緊要
對f1,f2,f3之間的執行順序沒有要求. 它們的執行結果不互相依賴,誰先完成誰后完成無關緊要. 但有一個函數f4,它必須等到f1,f2,f3執行完畢之后才能執行
對f1,f2,f3之間的執行順序有要求,必須要滿足f1->f2->f3的執行順序
下面就來簡單介紹一下,各個解決方案針對不同的業務場景,能解決什么問題
需求1對f1,f2,f3執行完成的順序沒有要求,即它們的執行結果是不互相依賴的,我們可以寫成如下的形式
f1(function(){}); f2(function(){}); f3(function(){}); ...需求2
f1,f2,f3之間執行完成的順序沒有要求,即它們各自的執行結果是不互相依賴的,但有一個函數f4,需要等f1,f2,f3函數全部執行完成之后才能執行
解決方法:`維護一個記數器`. f1,f2,f3的執行順序無關緊要,但對于f1,f2,f3每一個完成的回調里,都要判斷是否3個函數都已完成(通過count來判斷),如果都已完成,則執行f4. Ps(這里的寫成自執行的形式是防止count被污染) 實際上,node的三方異步管理模塊EventProxy, 以及promise的promise.all的實現,都是采用這種方式來對異步函數進行管理的. (function(){ let count = 0; function handler(){ if(count==3){ f4(); } } f1(function(){count++; handler();}); f2(function(){count++; handler();}); f3(function(){count++; handler();}); }()需求3
對于異步函數f1,f2,f3,我想保證它們的執行順序是f1->f2->f3的順序(即f1如果執行成功,調用f2,如果f2執行成功,調用f3)
3.1按最原始的方法,可以寫成如下回調嵌套的形式.即把f2作為f1的回調,f3作為f3的回調.依次嵌套就可以滿足f1->f2->f3這種調用形式. 這種方法雖然能夠滿足需求但同時存在很多問題: 回調層級太深,不好調試.
最簡單的情況,假設不考慮f1,f2,f3出錯的情況(即f1,f2,f3全部都執行正確),函數的執行流程大概是這樣:
f1(function(){ f2(function(){ f3(function(){ ... }) }) })
實際上,考慮到各個異步函數都有可能出錯的分支, 真實的執行流程應該是這樣(這才三層回調嵌套,代碼已經完全混亂的不能看了):
f1(function(){ if(err){ //f1 err handler } else{ f2(function(){ if(err){ //f2 err handler } else{ f3(function(){ if(err){ //f2 err handler } else{ ... } }) } }) } })3.2
為了解決這個嵌套過深這種問題,所以有了promise這種的解決方案. 這種規則邏輯比較清晰,更容易理解,但需要做一點點預備工作. 即異步函數f1,f2,f3全部要先封裝成promise規范,這里拿f1舉例(f2,f3同理).
function f1(){ var promiseObj = new Promise(function(resolve,reject){ //f1的具體功能代碼實現 ... if(f1err){ //如果f1執行出錯 reject(failValue); } else{ //如果f1執行成功 resolve(successValue); } }) return promiseObj; }
預備工作做完了,我們來看具體實現
f1() .then(function suc(){return f2()},function fail(){/*f1 err handler*/}) .then(function suc(){return f3()},function fail(){/*f2 err handler*/}) .then(function suc(){},function fail(){/*f3 err handler*/})
簡單來分析下,首先f1()執行完成后,會返回一個promise對象,它會被then捕獲,如果promise對象的狀態是resolve狀態,會調用then的第一個參數,即成功回調. 如果promise對象的狀態是reject狀態,會調用then的第二個參數,即失敗回調.
如果f1執行成功,則會在then中的成功回調suc中調用f2(),而f2()返回的也是一個promise對象,會被下一個then捕獲...依次類推
如果f1執行失敗,會在then的失敗回調fail中調用你寫的err handler句柄,然后return跳出整個執行鏈就可以
我們可以看到promise的語法實際上是將深度嵌套的邏輯通過then的處理平攤了.在這種語法規則下,f1->f2->f3的執行順序一目了然.當然它還是有缺點的,就像之前提到的,它必須要做一些預備工作,即需要把異步函數要封裝成promise規范. 另外,它還有一堆then,看起來有點頭暈
3.3既然promise我們也覺得有點麻煩,那只能試試es7的async/await了,聽說async/await+promise是管理異步回調的終極解決方案
首先來明晰下try/catch的概念. 當一個代碼片段,我們不能確定它到底能不能成功執行的情況下,就會用try/catch處理. 當fun函數自上到下執行,一開始會進入try{}塊,開始執行這個代碼片段
一旦try{}塊內部某一條代碼沒有正確執行,則不再執行try{}塊內部的代碼,而是立馬跳出try{}塊,同時會拋出一個異常,這個異常會被catch(){}捕獲. 開始執行catch{}塊里的代碼. 我們假設code2出錯了,整個函數內部的執行順序是 code 0 -> code 1 -> code 2-> code 4 -> code 5;
如果try{}塊內部的代碼片段全都正確執行了.就不會進入catch{}的錯誤處理流程了. 這時候整個函數內部的執行順序是 code 0 -> code 1 -> code 2-> code 3 -> code 5;
functionfun(){ /* code 0 */ try{ /* code 1 */ /* code 2 */ /* code 3 */ } catch(err){ /* code 4 */ } /* code 5 */ } fun();
對應到async上也是同理,async函數有一個特點,它的await能監聽一個promise對象. 如果監聽到的promise對象是resolve正確態,那么await這條語句相當于是被正確執行了,不會進入catch{}流程. 但如果監聽到的promise是reject錯誤態,則會認為await語句執行失敗了,會拋出異常然后跳進catch{}錯誤處理.
var funa = function(){ var promiseObj_a = new Promise(function(resolve,reject){ setTimeout(function(){resolve(1);},1000); }); return promiseObj_a; } var funb = function(){ var promiseObj_b = new Promise(function(resolve,reject){ setTimeout(function(){resolve(2);},5000) }); return promiseObj_b; } var func = function(){ var promiseObj_c = new Promise(function(resolve,reject){ setTimeout(function(){reject(3);},8000); }); return promiseObj_c; } async function testAsync(){ try { var a =await funa(); console.log(a,"resolve"); } catch(erra){ console.log(erra,"reject"); } try { var b =await funb(); console.log(b,"resolve"); } catch(errb){ console.log(errb,"reject"); } try { var c =await func(); console.log(c,"resolve"); } catch(errc){ console.log(errc,"reject"); } } testAsync(); //輸出結果是 //1 resolve //2 resolve //3 reject
我們能看到async/await配合promise帶來了巨大的好處. 首先異步函數的執行順序能夠像同步一樣一眼看出來,簡單明了. 其次,針對任何一個異步函數的執行,都有完善的try/catch機制,錯誤處理非常非常容易.
結言各種解決方案需要結合對應的業務場景使用
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/80184.html
摘要:前言業務開發中經常會用到異步函數,這里簡單的對異步函數以及它的各種各樣的解決方案做一個淺析優缺點優點能夠極大的提高程序并發業務邏輯的能力缺點異步函數的書寫方式和代碼執行邏輯很不直觀,回調函數這種方式不太符合人類的的線性思維異步函數的執行流程 前言 業務開發中經常會用到異步函數,這里簡單的對異步函數以及它的各種各樣的解決方案做一個淺析 優缺點: 優點: 能夠極大的提高程序并發業務邏輯的能...
摘要:前言業務開發中經常會用到異步函數,這里簡單的對異步函數以及它的各種各樣的解決方案做一個淺析優缺點優點能夠極大的提高程序并發業務邏輯的能力缺點異步函數的書寫方式和代碼執行邏輯很不直觀,回調函數這種方式不太符合人類的的線性思維異步函數的執行流程 前言 業務開發中經常會用到異步函數,這里簡單的對異步函數以及它的各種各樣的解決方案做一個淺析 優缺點: 優點: 能夠極大的提高程序并發業務邏輯的能...
摘要:任何數據結構只要部署接口,就可以完成遍歷操作即依次處理該數據結構的成員。的遍歷某個數據結構過程是這樣的比如對進行遍歷創建一個指針對象,指向當前數組的起始位置。 Iterator 這真是毅種循環 Iterator不是array,也不是set,不是map, 它不是一個實體,而是一種訪問機制,是一個用來訪問某個對象的接口規范,為各種不同的數據結構提供統一的訪問機制。任何數據結構只要部署Ite...
摘要:任何數據結構只要部署接口,就可以完成遍歷操作即依次處理該數據結構的成員。的遍歷某個數據結構過程是這樣的比如對進行遍歷創建一個指針對象,指向當前數組的起始位置。 Iterator 這真是毅種循環 Iterator不是array,也不是set,不是map, 它不是一個實體,而是一種訪問機制,是一個用來訪問某個對象的接口規范,為各種不同的數據結構提供統一的訪問機制。任何數據結構只要部署Ite...
閱讀 3265·2021-09-02 15:41
閱讀 2829·2021-09-02 09:48
閱讀 1368·2019-08-29 13:27
閱讀 1157·2019-08-26 13:37
閱讀 832·2019-08-26 11:56
閱讀 2479·2019-08-26 10:24
閱讀 1638·2019-08-23 18:07
閱讀 2615·2019-08-23 15:16