• 实现Promise的first等各种变体


    本篇文章主要是想通过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的理解。

  • 相关阅读:
    记一次文件上传远程服务器问题
    Python模块——loguru日志模块简单学习
    Python使用百度地图API根据地名获取相应经纬度
    Python用正则表达式匹配汉字
    【工具】下载与使用(感谢博友)
    【umask】安装产品,脚本中创建目录后注意刷权限
    【凝聚】引荐优笔
    【解决办法1】centos75 No manual entry for XXXX in section X
    【填坑1】CentOS7 systemctrl管理的服务,open files的神坑
    错题记录(二)
  • 原文地址:https://www.cnblogs.com/xumengxuan/p/10682400.html
Copyright © 2020-2023  润新知