JavaScript異步編程:協助弗利薩完成8次變身

這邊文章試圖經由過程一個例子展現javascript異步編程的幾種寫法。

示例申明

弗利薩必須要從第1形狀過渡到第8形狀才有能夠擊敗賽亞人,每一次變身成下一形狀須要1秒鐘,在這時期他能夠會遭遭到賽亞人的進擊,假如在變身過程當中遭到危險,他將被打回第一形狀。請協助弗利薩完整變身成第8形狀,擊敗賽亞人。

弗利薩的單例對象

最先建立一個弗利薩

 const Frieza = (function () {
            var state = 1; //內部變量,示意弗利薩的當前形狀
            return {
                chargingFlag: false, //示意弗利薩是不是正在變身中
                damage: function () {  //20%概率收到危險
                    return Math.random() < 0.2;
                },
                stateReset: function () { //打回第一形狀
                    state = 1;
                },
                getState: function () { //外部接見state的接口函數
                    return state;
                },
                change: function (callback) { //變身

                    if (this.chargingFlag === true) { 
                        throw new Error(`弗利薩還沒變身終了呢`);
                    }
                    this.chargingFlag = true;
                    console.log(`弗利薩最先舉行第${state + 1}形狀變身`)
                    setTimeout(() => { //每一階段變身斲喪1秒
                        if (this.damage()) {
                            this.stateReset();
                            this.chargingFlag = false;
                            callback('變身被悟空打斷啦!');
                            return;
                        }
                        state++;
                        this.chargingFlag = false;
                        callback(null, state);
                    }, 1000)
                }

            }
        })();

以上代碼用馬上實行函數建立了一個弗利薩

  • state為內部變量,示意弗利薩當前的形狀,初始為第一形狀,而且設置了一個接口(getState函數)供外部相識弗利薩當前的形狀。
  • change函數完成了弗利薩的變身,必需比及一次變身終了后才再次變身,每一次變身須要1秒鐘。
  • 在每一次變身終了後會實行回調函數,我們劃定回調函數有兩個參數,第一個示意變身失利,被打斷時應該傳入的參數,第二個示意變身勝利時應該傳入的參數。

接下來須要寫一些代碼來協助弗利薩堅持不懈的變身,直到他勝利變身到第8形狀。示例最終會按下圖的模樣在掌握台中顯現。

《JavaScript異步編程:協助弗利薩完成8次變身》

用Promise協助弗利薩:

        function keepChange() {
            return new Promise((resolve, reject) => {
                Frieza.change((err, state) => {
                    if (err) {
                        reject(err);
                    } else {
                        resolve(state);
                    }
                })
            })
        }
        function handelKeepChange() {
            keepChange().then((x) => {
             if(x !== 8){
                handelKeepChange();
             } else {
                 console.log('勝利!')
             }
        }).catch(err => {
            console.log(err);
            handelKeepChange();
        })
        }
       handelKeepChange();

看上去已不錯了,這已比直接在回調函數內里寫回調函數要好得多了,我們經由過程遞歸挪用handelKeepChange,讓這條Promise鏈延續到第八次變身終了。

用Promise + 生成器協助弗利薩

 // generator + promise

        function* async() {
            while (Frieza.getState() !== 8) {
                yield keepChange();
            }
            console.log('勝利!');
        }
        function keepChange() {
            return new Promise((resolve, reject) => {
                Frieza.change((err, state) => {
                    if (err) {
                        reject(err);
                    } else {
                        resolve(state);
                    }
                })
            })
        }
        function handleAsync(asyncFn) {
            const ita = asyncFn();
            function handle(v) {
                if (!v.done) {
                    v.value.then(state => {
                        handle(ita.next(state));
                    }).catch(err => {
                        console.log(err);
                        handle(ita.next());
                    })
                }
            }
            handle(ita.next());
        }
        handleAsync(async);

 

這類用生成器+promise的寫法比純用promise的寫法要龐雜一些,然則由於利用了生成器的特徵,使得我們在實行詳細的異步營業時,能夠寫的比較文雅:

        function* async() {
            while (Frieza.getState() !== 8) {
                yield keepChange();
            }
            console.log('勝利!');
        }

這類寫法比較有親和力,邏輯上比較清楚。它內部的完成是經由過程一個handleAsync函數不斷地遞歸挪用handle函數,從而讓生成器能在一次Promis許諾完成后讓生成器繼承產出下一次Promise。

            function handle(v) {
                if (!v.done) { // 假如生成器還沒完畢,那末就繼承產出一個promise
                    v.value.then(state => {
                        handle(ita.next(state));
                    }).catch(err => {
                        console.log(err);
                        handle(ita.next());
                    })
                }
            }

用async await協助弗利薩

async await是promise+生成器的語法層面完成。能夠讓我們省略背地的細節,直接採納同步寫法編寫異步順序。

        async function handleAsync() {
            while(Frieza.getState() !== 8){
                try {
                    await keepChange();
                } catch (error) {
                    console.log(error)
                }
            }
            console.log('勝利!');
           
        } 
        handleAsync(); */

如許就能夠了,險些與promise+與生成器的營業寫法如出一轍。

用rxjs協助弗利薩

用上rxjs的觀察者形式后,實際上就能夠把弗利薩的change函數內里的callback給解耦出來。把這部份的邏輯交給觀察者處置懲罰內里。而弗利薩只要在每次變身勝利或許失利時發出通知就好了。
詳細步驟以下

  1. 建立一個能夠供人人收看的電視節目’dragonBall’,這個被我們叫做七龍珠的電視節目(subject)能夠被觀眾們定閱,同時,這個電視節目也能為所欲為的播放他想要給觀眾們看到的東西。

    const dragonBall = new Rx.Subject();
  2. 讓弗利薩在變身完成或失利時,經由過程dragnonBall這個subject,示知一切收看該節目的觀眾他變身失利,或許勝利了。修正弗利薩的change函數:

                change: function () {
    
                    if (this.chargingFlag === true) {
                        drangonBall.next(new Error('變身還沒完畢呢!'))
                    }
                    this.chargingFlag = true;
                    console.log(`弗利薩最先舉行第${state + 1}形狀變身`)
                    setTimeout(() => {
                        if (this.damage()) {
                            this.stateReset();
                            this.chargingFlag = false;
                            dragonBall.next(new Error('變身被悟空打斷啦!'));
                            return;
                        }
                        state++;
                        this.chargingFlag = false;
                        dragonBall.next(`${state}形狀變身勝利!`)
                    }, 1000)
                }
  3. 收看dragonBall,而且在弗利薩沒變到第8形狀前,延續地讓弗利薩變身。

            const watchAnime = dragonBall.asObservable()
            
            .subscribe(message => {
                console.log(message);
                
                if (Frieza.getState() !== 8) {
                    Frieza.change();
                } else {
                    watchAnime.unsubscribe();
                }
    
            })
    
  4. 讓弗利薩最先變身

       Frieza.change();

前去github檢察示例代碼

    原文作者:野生爬山虎
    原文地址: https://segmentfault.com/a/1190000015343530
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞