js異步任務(wù)隊列

?最近在一個項目中蝇闭,遇到這么一個需求:一個頁面中罕偎,大概有四五個元素需要按一定次序依次進場,setTimeout來實現(xiàn)吧陌知,仔細一想他托,那樣的代碼實在是寫不下去,大概是這樣的:

setTimeout(()=>{
    this.setState({view1Visible: true})
    setTimeout(()=>{
        this.setState({view2Visible: true})
        setTimeout(()=>{
            this.setState({view3Visible: true})
            setTimeout(()=>{
                // 沒完沒了的setTimout...
            },500)
        },500)
    },500)
},100)

?明顯的回調(diào)地獄仆葡,對癥下藥赏参,用Promise來簡單封裝一下:

const timer=(task,ms)=>{
    return new Promise((resolve,reject)=>{
        setTimeout(()=>{
            task && task()
            resolve()
        },ms)
    })
}

?然后之前的代碼大致可以寫成這樣:

timer(()=>this.setState({view1Visible: true}),100)
    .then(()=>timer(()=>this.setState({view2Visible: true}),500))
    .then(()=>timer(()=>this.setState({view3Visible: true}),500))
    .then(()=>timer(()=>this.setState({view4Visible: true}),500))

?到這里基本已經(jīng)滿足我的需求了,如果對不喜歡用then沿盅,或者只是對它有意見把篓,也可以用async/await來改寫一下:

async layout(){
    await timer(()=>this.setState({view1Visible: true}),100)
    await timer(()=>this.setState({view2Visible: true}),500)
    await timer(()=>this.setState({view3Visible: true}),500)
    await timer(()=>this.setState({view4Visible: true}),500)
}

?寫到這里,已經(jīng)足夠了腰涧,不過我個人對timer的兩個參數(shù)不喜歡纸俭,而且我更喜歡寫鏈式風格的代碼,理想的代碼是這樣的:

new Schedule()
    .delay(100).task(()=>this.setState({view1Visible:true}))
    .task(()=>this.setState({view1Visible:true}))
    .task(()=>this.setState({view2Visible:true}))
    .task(()=>this.setState({view3Visible:true}))

?首先task和delay分別用兩個方法傳參南窗,語義化嘛,一眼就能看出這個參數(shù)指的是什么郎楼;然后delay要能夠復(fù)用万伤,很多情下我們?nèi)蝿?wù)之間的間隔是相等的,就不用每次都傳了呜袁。

?實現(xiàn)方法嘛敌买,在Schedule類中,要有個promise來處理這些任務(wù)阶界,然后需要一個變量來保存delay虹钮,來達到復(fù)用的目的,然后就是delay和task兩個方法膘融,都返回this來實現(xiàn)鏈式調(diào)用芙粱。最后把上面那個timer方法拿過來,解決回調(diào)地獄氧映。先看看最后的代碼吧:

export default class Schedule{
  constructor(){
    this._delay=0
    this.p = null
  }
  timer(task,ms){
    return new Promise((resolve,reject)=>{
      setTimeout(()=>{
        task && task()
        resolve()
      },ms)
    })
  }
  task(task){
    const {_delay:delay,timer,p}=this
    this.p = p ?p.then(()=>timer(task,delay)) :timer(task,delay)
    return this
  }
  delay(_delay){
    this._delay = _delay
    return this
  }
}

?也沒啥特別的春畔,要注意的一點是,第一次調(diào)用task的時候岛都,p為空律姨,直接給他賦值即可【室撸或者你一可以給p一個初始的promise择份,之后就不用考慮是否為空了,直接p.then()就可以了烫堤,需要先用一個臨時變量把delay緩存起來荣赶,否則最后再執(zhí)行到當前task的時候凤价,delay很有可能取到的是后面賦的值。

?對于一般的需求讯壶,現(xiàn)在這個Schedule應(yīng)該完全能夠搞定料仗,可能你想這樣做:先把任務(wù)隊列定義好,到了特定的時機再去觸發(fā)它執(zhí)行伏蚊,那我們要怎么做呢立轧?

?其實也不難,每次調(diào)用task的時候躏吊,不放到promise里面氛改,而是把task和當前delay先保存到一個數(shù)組里面,最后再寫一個方法比伏,在調(diào)用的時候遍歷這個數(shù)組胜卤,把他們放到promise里面去,直接上代碼好了:

export default class Schedule{
  constructor(){
    this._delay=0
    this.tasks=[]
  }
  timer(task,ms){
    return new Promise((resolve,reject)=>{
      setTimeout(()=>{
        task && task()
        resolve()
      },ms)
    })
  }
  task(task){
    this.tasks.push({task,delay:this._delay})
    return this
  }
  delay(_delay){
    this._delay = _delay
    return this
  }
  exec(){
    this.tasks.length>0 && this.tasks.reduce(
      (p,t)=>p.then(()=>this.timer(t.task,t.delay)),
      Promise.resolve()
    )
  }
}

?一個小小的技巧就是用數(shù)組的reduce方法來把這些task依次放到promise中赁项,在reduce的第二個參數(shù)傳入一個空的Promise葛躏,就避免了判斷是否有初始Promise的問題。用的時候需要手動去調(diào)用exec方法悠菜,整個隊列才回開始執(zhí)行:

new Schedule()
    .delay(100).task(()=>this.setState({view1Visible:true}))
    .task(()=>this.setState({view1Visible:true}))
    .task(()=>this.setState({view2Visible:true}))
    .task(()=>this.setState({view3Visible:true}))
    .exec() // 可以在任何你需要的時候調(diào)用

?需要介紹的就這些了舰攒,最后其實有不少可以改進的地方,比如上面說的兩種情況悔醋,完全可以寫在一起摩窃,構(gòu)造方法中傳個參數(shù)來決定是否是需要延遲執(zhí)行的隊列。又或者引入cron表達式芬骄,來決定在特定的時間點執(zhí)行任務(wù)……當然這些不在本文討論的范疇猾愿,感興趣的朋友可以去試試。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末账阻,一起剝皮案震驚了整個濱河市蒂秘,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌淘太,老刑警劉巖材彪,帶你破解...
    沈念sama閱讀 217,826評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異琴儿,居然都是意外死亡段化,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,968評論 3 395
  • 文/潘曉璐 我一進店門造成,熙熙樓的掌柜王于貴愁眉苦臉地迎上來显熏,“玉大人,你說我怎么就攤上這事晒屎〈。” “怎么了缓升?”我有些...
    開封第一講書人閱讀 164,234評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長蕴轨。 經(jīng)常有香客問我港谊,道長,這世上最難降的妖魔是什么橙弱? 我笑而不...
    開封第一講書人閱讀 58,562評論 1 293
  • 正文 為了忘掉前任歧寺,我火速辦了婚禮,結(jié)果婚禮上棘脐,老公的妹妹穿的比我還像新娘斜筐。我一直安慰自己,他們只是感情好蛀缝,可當我...
    茶點故事閱讀 67,611評論 6 392
  • 文/花漫 我一把揭開白布顷链。 她就那樣靜靜地躺著,像睡著了一般屈梁。 火紅的嫁衣襯著肌膚如雪嗤练。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,482評論 1 302
  • 那天在讶,我揣著相機與錄音煞抬,去河邊找鬼。 笑死真朗,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的僧诚。 我是一名探鬼主播遮婶,決...
    沈念sama閱讀 40,271評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼湖笨!你這毒婦竟也來了旗扑?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,166評論 0 276
  • 序言:老撾萬榮一對情侶失蹤慈省,失蹤者是張志新(化名)和其女友劉穎臀防,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體边败,經(jīng)...
    沈念sama閱讀 45,608評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡袱衷,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,814評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了笑窜。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片致燥。...
    茶點故事閱讀 39,926評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖排截,靈堂內(nèi)的尸體忽然破棺而出嫌蚤,到底是詐尸還是另有隱情辐益,我是刑警寧澤,帶...
    沈念sama閱讀 35,644評論 5 346
  • 正文 年R本政府宣布脱吱,位于F島的核電站智政,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜澎胡,卻給世界環(huán)境...
    茶點故事閱讀 41,249評論 3 329
  • 文/蒙蒙 一谒出、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧疾忍,春花似錦、人聲如沸床三。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,866評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至四瘫,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間找蜜,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,991評論 1 269
  • 我被黑心中介騙來泰國打工洗做, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留弓叛,地道東北人诚纸。 一個月前我還...
    沈念sama閱讀 48,063評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像畦徘,于是被迫代替她去往敵國和親毕籽。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,871評論 2 354

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

  • 那些年我在乎過很多,喜歡過很多杯缺,嘗試過看得到的新鮮平委。只是為了一個很簡單的理由,后來那些在乎不知去了何方夺谁。后來我就開...
    夜落y閱讀 528評論 0 5
  • 1 我出生在蘇北地區(qū)廉赔,挨著山東肉微。從小耳聞目睹家務(wù)都是婦女來干,洗衣服蜡塌、做飯碉纳,不夸張地說男人們油瓶倒了都不帶扶的。 ...
    流年芳華閱讀 1,550評論 2 5
  • 2017.12.08 編號28 日精進554天 體驗入: 愛的三大表現(xiàn):1希望他好馏艾! 2愿意犧牲自己為他好劳曹! 3愿...
    宇宙之愛黃昊貴閱讀 261評論 0 0
  • 當走出學(xué)院推免面試的門的時候铁孵,我知道我可以了。 我以為我會擠下幾滴眼淚房资,就像一個月之前在重慶的酒店里蜕劝,離開重慶之前...
    real_x閱讀 384評論 0 0