不那么隨處可見的JavaScript學(xué)習(xí)筆記-漫談Promise&&async&&職責(zé)鏈

嚴(yán)肅活潑,團(tuán)結(jié)緊張。戰(zhàn)天斗地,為民為邦。

題圖--引自網(wǎng)絡(luò),侵刪,請聯(lián)系我

引言

實(shí)踐是檢驗(yàn)真理的唯一標(biāo)準(zhǔn)

在相當(dāng)長的一段時間內(nèi),對于Promise和async,我會用,但是僅僅限于實(shí)現(xiàn)一個功能,而沒有深究它的表現(xiàn),
比如我們換種寫法它會不會有不同的表現(xiàn),是不是有更好的寫法來應(yīng)對我當(dāng)前的業(yè)務(wù)場景?
所以在今天,我將回歸初心對這些方面進(jìn)行探索,介紹下職責(zé)鏈模式,因?yàn)槲腋杏X,在我現(xiàn)在遇到的場景下可以套用這種編程范式。
之后我將對我的感覺進(jìn)行驗(yàn)證。

從回調(diào)的N種寫法說起

作為一個前端,對Ajax不可謂不熟,我剛工作那會甚至有種說法,你是3k的切圖崽還是5k的切圖崽只取決于你是否了解Ajax。
不得不說通過Ajax以Json數(shù)據(jù)的形式來進(jìn)行前后端交互在真正意義上將前端分離成一個職位。
眾所周知Js是單線程異步的,在這里回調(diào)就應(yīng)用而生。
xhr的寫法我已記不太清,我們就以jQuery來舉例。

$.ajax{
    url:'...',
    date:'...',
    type:'post',
    success:function(value){
        ....
        //這里就是回調(diào)
    }
}

這是一個非常常見的場景,假如,我們要先去地址A請求,獲取到數(shù)據(jù)再去地址B請求得到數(shù)據(jù),再去地址C請求得到一個數(shù)據(jù)....
最后在執(zhí)行業(yè)務(wù)邏輯會變成什么樣呢?

$.ajax{
    url:'...A',
    date:'...',
    type:'post',
    success:function(value){
        ....
        //這里就是回調(diào)
        $.ajax{
            url:'...B',
            date:'...',
            type:'post',
            success:function(value){
                ....
                //這里就是回調(diào)
                        $.ajax{
                        url:'...C',
                        date:'...',
                        type:'post',
                        success:function(value){
                            ....
                            //這里就是回調(diào)
                        }
                    }
            }
        }
    }
}

回調(diào)地獄就這么誕生了..也許你可以封裝一下,讓它好看一點(diǎn),然而這樣的代碼無論是讀起來還是改起來依舊是讓人感覺身處地獄。
幸運(yùn)的是我工作的時候jQuery已經(jīng)是2.X了,它的AJAX實(shí)現(xiàn)了Promise。所以,實(shí)際中我不必去面對這種代碼。

var promise = $.ajax{
    url:'...A',
    date:'...',
}
promise.then(function(value){
    ....
    return $.ajax{
        url:'...B',
        date:'...'
    }
});
promise.then(function(value){
    ...
    return $.ajax{
        url:'...C',
        date:'...'
    }
})
promise.then(function(value){
    ....
})

雖然看起來不怎么優(yōu)雅但是比回調(diào)地獄還是強(qiáng)太多了,不是么。
我不太清楚jQuery實(shí)現(xiàn)的promise有沒有race方法,現(xiàn)在也并無動力去驗(yàn)證。
之后出現(xiàn)了一種,generator yeild 的寫法。我并沒有應(yīng)用于實(shí)踐中過,它看起來是這個樣子的...

function* job(){
    yeild 某種異步操作..
    yeild 某種異步操作...
     .....
}

使用yeild的話,那個異步操作會相當(dāng)于被轉(zhuǎn)換為同步的,停留在那等待異步返回才會去執(zhí)行下面的操作,個人感覺結(jié)合Promise會更好用。
雖然我沒用過,接下來就是async和await。
與其說它是新的語法,不如說是generator yeild的語法糖,它看起來通常是這樣的。

var job = async(){
    await 某種異步操作..
    await 某種異步操作..
    ...
}

幾乎和上面如出一轍,換了個寫法罷了,在實(shí)踐中我?guī)缀跏墙Y(jié)合著Promise來用。
至此,從使用上來說,已并無大礙,基本寫法就是這么簡單,但是人人都止步于此的話,誰來開發(fā)那些好用的框架呢?
所以下面讓我稍微哦深入學(xué)習(xí)一下。

Promise

從這里開始,默認(rèn)是ES6的實(shí)現(xiàn)。
簡單來說Promise就是一個可以處理異步操作的對象,提供操作這個異步操作的API,通常會保存這個異步操作的狀態(tài),同時狀態(tài)的改變不可逆。
ES6中Promise是一個構(gòu)造函數(shù),通常的用法:

var pormise = new Promise((resolve,reject)=>{
    //做一些異步操作如果成功
    resolve();
    //如果失敗 則調(diào)用reject
})
//resolve會使這個promise對象狀態(tài)變成成功結(jié)束。
//reject則使這個promise對象狀態(tài)變成失敗結(jié)束。

rejcet 的話會返回一個新的promise對象,在這里你可以做一些彌補(bǔ)。
掌握這些,當(dāng)你需要進(jìn)行異步操作的時候,這些就已經(jīng)足夠了,但是如果你想寫出健壯的代碼還需要掌握。

var pormise = new Promise((resolve,reject)=>{
    //做一些異步操作如果成功
    resolve();
    //如果失敗 則調(diào)用reject
})

pormise.then((value)=>{
   //對這個數(shù)據(jù)作一些事情
   
})
promise.catch((err)=>{
    //錯誤處理
})
promise.finally(()=>{
    //無論狀態(tài)如何都會執(zhí)行
    //文檔中說似乎無法通過參數(shù)獲得狀態(tài),我覺得,在這里寫些作為錯誤處理的兜底的代碼應(yīng)該可行。
})

到這里,如果處理得當(dāng)你的promise已經(jīng)足夠健壯,不會因?yàn)橐恍┬★L(fēng)浪而翻船。
假如你想更加優(yōu)雅的處理回調(diào)地獄,你還需要了解。

//Promise.all
const p = Promise.all([p1, p2, p3]);
//它會把多個promise包裝成一個大promise,它們是同時執(zhí)行的,只有在都結(jié)束的時候,才會調(diào)用p的then
//Pomise.race
const p = Promise.race([p1, p2, p3]);
//和上面的區(qū)別是這里是順序執(zhí)行的。
//需要注意的是,上面兩個方法都是執(zhí)行一組promise,如果有一個promise為reject狀態(tài)的話,那么它們整體,也就是說這個p也將是reject狀態(tài)。

至此無論是并發(fā),還是鏈?zhǔn)秸{(diào)用都并無大礙了,剩下的就是在實(shí)踐中去應(yīng)用了,平心而論它們看起來還是蠻簡單的。

async+await

同步與等待,這主要解決的是異步的異步問題,異步照理說是個優(yōu)點(diǎn),但是在一些場景我們往往不希望它返回的那么快,
async await便應(yīng)用而生。

//基本用法就像之前說的
async function job(){
    await p1
    await p2 
    await p3
    ...
}
//這里Job也會是一個Promise對象

基本可以看做generator的語法糖,用Promise.race也能做到類似的用法,問題是race不怎么好阻塞,而且明顯async的寫法要更好處理一些。

//稍微需要注意下錯誤處理
async function job(){
    try{
        await p1
        await p2 
        await p3 
    }.catch(err){
        ....
    }

    ...
}
job.then(()=>{
    
});
job.catch(()=>{

})
//p1,p2,p3一起做錯誤處理,之后job自身也可以錯誤處理。
//不過我更推薦分開進(jìn)行,直接使用promise的catch方法。
//需要并發(fā)的話,在內(nèi)部還能使用Promise.all

不得不說,async await結(jié)合promise能用更簡潔優(yōu)雅的代碼玩出更多的花樣,這對我之后重構(gòu)代碼將有很大的幫助。

職責(zé)鏈模式

定義:使多個對象都有機(jī)會處理請求,從而避免請求的發(fā)送者和對象之間的耦合關(guān)系。
什么意思呢?就是說將多個對象連成鏈條,然后傳一個請求進(jìn)來,總有一個能解決它。
直白來說就是使用多態(tài)來重構(gòu)的switch case。

//隨便舉個列子
//假設(shè)一個購買活動,有幾種購買可能。
//比如:預(yù)付200得到優(yōu)惠券50 預(yù)購300得到100優(yōu)惠券 預(yù)付100沒有優(yōu)惠 
//通常來說一個巨大的sitch case可以解決這個問題,問題是這違反了開放-封閉原則,不利于擴(kuò)展。

var order200=(pay)=>{
    if(pay===200){
        console.log('預(yù)付200,得到50優(yōu)惠券')
    }else{
        return 'nextSuccessor' //像后傳遞。
    }
}
var order300=(pay)=>{
    if(pay===300){
        console.log('預(yù)付300,得到100優(yōu)惠券')
    }else{
        return 'nextSuccessor' //像后傳遞。
    }
}
var order100=(pay)=>{
    if(pay===100){
        console.log('預(yù)付100,無優(yōu)惠券')
    }else{
        return 'nextSuccessor' //像后傳遞。
    }
}

var Chain =function (fn){
    this.fn=fn;
    this.successor=null
}

Chain.prototype.setNextSuccessor =function (successor){
    return this.successor = successor;
}
Chain.prototype.passRequest  = function (){
    var ret = this.fn.apply(this,arguments);
    if(ret === 'nextSuccessor'){
        return this.successor && this.successor.passRequest.apply(this.successor,arguments);
    }
    return ret;
}
//包裝節(jié)點(diǎn)
var cOrder200=new Chain(order200);
var cOrder300=new Chain(order300);
var cOrder100=new Chain(order100);
//指定順序
cOrder100.setNextSuccessor(cOrder200);
cOrder200.setNextSuccessor(cOrder300);
//調(diào)用
cOrder100.passRequest(100);
cOrder100.passRequest(200);
cOrder100.passRequest(300);

這段代碼有種為了舉例而舉例,頗有殺雞用牛刀的意思,不過很好懂。
值得一提的是,在這里踩了兩個新語法糖。
因?yàn)樽饔糜虻年P(guān)系箭頭函數(shù)不能作為構(gòu)造函數(shù)來使,也不適合用來掛在prototype上。
這么看來為解決自調(diào)問題產(chǎn)生的箭頭函數(shù),設(shè)計(jì)的很精巧,它能且只能解決自調(diào)問題。

結(jié)語

emmmm..到最后我發(fā)現(xiàn)Promise和async await 雖然結(jié)合密切,但是與后面的職責(zé)鏈似乎并不關(guān)聯(lián)。
我想處理的場景也是更類似鏈?zhǔn)交卣{(diào)而不是職責(zé)鏈。
假如在職責(zé)鏈里加入一個手動的Next方法,我也能處理異步問題.....
這里似乎又可以引入命令模式。
所謂學(xué)的越多不會的越多大概就是這么一回事吧。
我現(xiàn)在除了代碼重構(gòu)的規(guī)則,又多了個命令模式想要探究。
只是這篇筆記已經(jīng)夠長了,這么看來明天學(xué)什么也有著落了。
按照這個進(jìn)度大概周末我可以開始著手重構(gòu)之前的爬蟲代碼,豈不是正好?

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,663評論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,125評論 3 414
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,506評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,614評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,402評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 54,934評論 1 321
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,021評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,168評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,690評論 1 333
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,596評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,784評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,288評論 5 357
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,027評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,404評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,662評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,398評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,743評論 2 370

推薦閱讀更多精彩內(nèi)容

  • 異步編程對JavaScript語言太重要。Javascript語言的執(zhí)行環(huán)境是“單線程”的,如果沒有異步編程,根本...
    呼呼哥閱讀 7,321評論 5 22
  • Promise的含義: ??Promise是異步編程的一種解決方案,比傳統(tǒng)的解決方案——回調(diào)函數(shù)和事件——更合理和...
    呼呼哥閱讀 2,182評論 0 16
  • Promiese 簡單說就是一個容器,里面保存著某個未來才會結(jié)束的事件(通常是一個異步操作)的結(jié)果,語法上說,Pr...
    雨飛飛雨閱讀 3,366評論 0 19
  • async 函數(shù) 含義 ES2017 標(biāo)準(zhǔn)引入了 async 函數(shù),使得異步操作變得更加方便。 async 函數(shù)是...
    huilegezai閱讀 1,267評論 0 6
  • 弄懂js異步 講異步之前,我們必須掌握一個基礎(chǔ)知識-event-loop。 我們知道JavaScript的一大特點(diǎn)...
    DCbryant閱讀 2,738評論 0 5