實現Promise的first等各種變體

原文地址: https://www.xiabingbao.com/po...

本篇文章主要是想通過ES6中Promise提供的幾個方法,來實現諸如first、last、none、any等各種變體方法!

在標準的ES6規範中,提供了Promise.allPromise.race兩種,我們首先來了解下這兩個方法是幹嘛的,方便我們後面工作的展開。Promise.all中所有的Promise實例都處於完成狀態,該方法才進入完成狀態,否則任意一個被拒絕,則該方法進入拒絕狀態,並捨棄其他所有完成的結果,拒絕原因是第一個被拒絕的實例的原因。Promise.race中任意的一個Promise實例變成完成狀態或者拒絕狀態,則race結束,race的結果即爲第一個變成最終狀態的結果!更詳細的可以參考下阮一峯的文章Promise對象之Promise.all

1. 準備工作

在開始編寫各種變體方法之前,這裏我們首先定義幾個一會兒要使用的幾個Promise實例:

/**
 * 創建一個Promise對象的實例
 * @param name {string} 該實例的名稱
 * @param flag {boolean} 返回的結果狀態:完成還是拒絕
 * @param diff {number} 延遲的時間
 */
var createPromiseCase = ( name, flag, diff ) => {
    return new Promise( ( resolve, reject ) => {
        setTimeout( () => {
            flag ? resolve( name ) : reject( new Error( 'testPromise is error, name: ' + name ) );
        }, diff );
    } );
};

var p1_suc_100 = createPromiseCase( 'p1-suc-100', true, 100 );
var p2_suc_500 = createPromiseCase( 'p2-suc-500', true, 500 );
var p3_suc_300 = createPromiseCase( 'p3-suc-300', true, 300 );
var p4_fail_400 = createPromiseCase( 'p4-fail-400', false, 400 );
var p5_fail_200 = createPromiseCase( 'p5-fail-200', false, 200 );

2. 各種變體方法

2.1 Promise.first

場景:一個頁面當前正處於loading狀態,同時請求了多個接口,無論哪個接口正確返回結果,則loading效果取消!或者其他的要獲取獲取第一個完成狀態的值。

這裏就要用到了Promise.first了,只要任意一個Promise實例變成完成狀態,則Promise.first變成完成狀態。其實這裏並不適合Promise.race方法,因爲第一個變成拒絕狀態的實例也會激活Promise.race,

if ( !Promise.first ) {
    // get first resolve result
    Promise.first = promiseList => {
        return new Promise( ( resolve, reject ) => {
            var num = 0;
            var len = promiseList.length;
            promiseList.forEach( pms => {
                Promise.resolve( pms ).then( resolve ).catch( () => {
                    num++;
                    if ( num === len ) {
                        reject( 'all promises not resolve' );
                    }
                } );
            } );
        } );
    };
}

調用方式:

Promise.first([p4_fail_400, p2_suc_500, p3_suc_300])
    .then(res => console.log(res)) // p3-suc-300
    .catch(e => console.error(e))

可以看到每次獲取的p3_suc_300的值,因爲p4是失敗的狀態,p2的完成狀態沒有p3快,因此這裏獲取到了p3的結果。

2.2 Promise.last

與Promise.first對應的則是Promise.last,獲取最後變成完成狀態的值。這裏與Promise.first不同的是,只有最後一個Promise都變成最終態(完成或拒絕),才能知道哪個是最後一個完成的,這裏我採用了計數的方式,thencatch只能二選一,等計數器達到list.length時,執行外部的resolve。

if ( !Promise.last ) {
    // get last resolve result
    Promise.last = promiseList => {
        return new Promise( (resolve, reject) => {
            let num = 0;
            let len = promiseList.length;
            let lastResolvedResult;

            const fn = () => {
                if (++num===len) {
                    lastResolvedResult ? resolve(lastResolvedResult) : reject('all promises rejected');
                }
            }
            promiseList.forEach( pms => {
                Promise.resolve( pms )
                    .then(res => {
                        lastResolvedResult = res;
                        fn()
                    })
                    .catch(fn);
            } )
        } )
    }
}

調用方式:

Promise.last([p1_suc_100, p2_suc_500, p5_fail_200, p3_suc_300, p4_fail_400])
    .then(res => console.log(res)) // p2-suc-500
    .catch(e => console.error(e))

p2需要500ms才能完成,是最晚完成的。

2.3 Promise.none

Promise.none與Promise.all正好相反,所有的promise都被拒絕了,則Promise.none變成完成狀態。該方法可以用Promise.first來切換,當執行Promise.first的catch時,則執行Promise.none中的resolve。不過這裏我們使用Promise.all來實現。

if ( !Promise.none ) {
    // if all the promises rejected, then succes
    Promise.none = promiseList => {
        return Promise.all( promiseList.map( pms => {
            return new Promise( ( resolve, reject ) => {
                // 將pms的resolve和reject反過來
                return Promise.resolve( pms ).then( reject, resolve );
            } )
        } ) )
    }
}

調用方式:

Promise.none([p5_fail_200, p4_fail_400])
    .then(res => console.log(res))
    .catch(e => console.error(e))

// then的輸出結果:
// [
//     Error: testPromise is error, name: p5-fail-200, 
//     Error: testPromise is error, name: p4-fail-400
// ]

兩個promise都失敗後,則Promise.none進入完成狀態。

2.4 Promise.any

Promise.any表示只獲取所有的promise中進入完成狀態的結果,被拒絕的則忽略掉。

if ( !Promise.any ) {
    // get only resolve the results
    Promise.any = promiseList => {
        let result = [];
        return Promise.all( promiseList.map( pms => {
            return Promise.resolve( pms )
                .then( res => result.push( res ) )
                .catch( e => { } );
        } ) ).then( ( res ) => {
            return new Promise( ( resolve, reject ) => {
                result.length ? resolve( result ) : reject();
            } )
        } )
    }
}

調用方式:

Promise.any([p1_suc_100, p2_suc_500, p5_fail_200, p3_suc_300, p4_fail_400])
    .then(res => console.log(res)) // ["p1-suc-100", "p3-suc-300", "p2-suc-500"]
    .catch(e => console.error(e))

2.5 Promise.every

最後一個的實現比較簡單,所有的promise都進入完成狀態,則返回true,否則返回false。

if (!Promise.every) {
    // get the boolean if all promises resolved
    Promise.every = promiseList => {
        return Promise.all(promiseList)
            .then(() => Promise.resolve(true))
            .catch(() => Promise.resolve(false));
    }
}

調用方式:

Promise.every([p1_suc_100, p2_suc_500, p3_suc_300])
    .then(result => console.log('Promise.every', result)); // Promise.every true

Promise.every([p1_suc_100, p4_fail_400])
    .then(result => console.log('Promise.every', result)); // Promise.every false

3. 總結

Promise還有各種方面的應用,很多類庫也都實現了類似的方法,這裏也僅僅是鄙人拙見,稍微實現了Promise的變體方法,加深下對Promise的理解。

原文地址: 蚊子的博客

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章