zoukankan      html  css  js  c++  java
  • 实现简易Promise

    概述

    异步编程离不开promise, async, 事件响应这些东西,为了更好地异步编程,我打算探究一下promise的实现原理,方法是自己实现一个简易的promise。

    根据promise mdn上的描述,我们主要实现如下api:

    1. Promise.prototype.resolve
    2. Promise.prototype.reject
    3. Promise.prototype.then
    4. Promise.all
    5. Promise.race

    为了更好地性能和使用,我还需要加上惰性求值特性,即:只有调用then的时候才真正调用Promise里面的异步方法。并且我们忽略pending,fulfilled和rejected这几个状态,因为封装后的promise并没有暴露这几个状态,目前看来也没什么用(除非用事件响应实现Promise)。

    为了简便,暂时不考虑错误处理

    实现resolve,reject和then

    其实就是调用resolve和reject的时候调用相应的success和error函数就行,代码如下:

    let Promise = function(func) {
        this.func = func;
        this.success = null;
        this.error = null;
    }
    
    Promise.prototype.resolve = function(value, that) {
        console.log('resolve');
        if(typeof that.success == 'function') {
            that.success(value);
        }
    }
    
    Promise.prototype.reject = function(value, that) {
        console.log('reject');
        if(typeof that.error == 'function') {
            that.error(value);
        }
    }
    
    Promise.prototype.then = function(onFulfilled, onRejected) {
        this.success = onFulfilled;
        this.error = onRejected;
        setTimeout(() => {
            this.func(this.resolve, this.reject);
        });
    }
    
    let myPromise = new Promise(function(resolve, reject){
        setTimeout(() => {
            resolve("成功!", this);
        }, 1000);
    });
    
    myPromise.then((successMessage) => {
        console.log('输出', successMessage);
    });
    

    需要注意的是,这里如果不带入this的话,resolve里面的this就会丢失。

    但是这么写不优雅,我想了很多办法,比如重新包装一下,比如用事件响应,但还是解决不了,最后我突然想到,用bind,哇,成功解决。代码如下:

    let Promise = function(func) {
        this.func = func;
        this.success = null;
        this.error = null;
    }
    
    Promise.prototype.resolve = function(value) {
        if(typeof this.success == 'function') {
            this.success(value);
        }
    }
    
    Promise.prototype.reject = function(value) {
        if(typeof this.error == 'function') {
            this.error(value);
        }
    }
    
    Promise.prototype.then = function(onFulfilled, onRejected) {
        this.success = onFulfilled;
        this.error = onRejected;
        setTimeout(() => {
            this.func(this.resolve.bind(this), this.reject.bind(this));
        });
    }
    
    let myPromise = new Promise(function(resolve, reject){
        setTimeout(() => {
            resolve("成功!", this);
        }, 1000);
    });
    
    myPromise.then((successMessage) => {
        console.log('输出', successMessage);
    });
    

    值得一提的是,为了实现惰性求值,需要先把异步方法缓存起来,等调用then的时候再调用它。

    还有,在Promise内部,为了简便,我使用的是setTimeout进行异步,并没有使用setImmediate

    实现all和race

    all和race在多异步promise里面非常有用,下面我们来实现它们:

    Promise.all = function(promiseArr) {
        let results = [];
        let sum = promiseArr.length;
        let count = 0;
        return new Promise(function(resolve, reject) {
            if(promiseArr || sum) {
                for(let i=0; i<sum; i++) {
                    promiseArr[i].then((res) => {
                        results[i] = res;
                        count ++;
                        if(count >= sum) {
                            resolve(results);
                        }
                    });
                }
            }
        });
    };
    
    Promise.race = function(promiseArr) {
        let sum = promiseArr.length;
        let count = 0;
        return new Promise(function(resolve, reject) {
            if(promiseArr || sum) {
                for(let i=0; i<sum; i++) {
                    promiseArr[i].then((res) => {
                        if(count == 0) {
                            count ++;
                            resolve(res);
                        }
                    });
                }
            }
        });
    };
    

    可以看到,方法是使用传说中的哨兵变量,真的很有用。

    测试

    简易的测试代码如下:

    let myPromise1 = new Promise(function(resolve, reject){
        setTimeout(() => {
            resolve("成功1111111!");
        }, 1000);
    });
    
    let myPromise2 = new Promise(function(resolve, reject){
        setTimeout(() => {
            resolve("成功222222222222!");
        }, 1500);
    });
    
    myPromise1.then((successMessage) => {
        console.log('输出', successMessage);
    });
    
    console.time('all计时开始');
    Promise.all([myPromise1, myPromise2]).then((results) => {
        results.map(item => {
            console.log('输出', item);
        });
        console.timeEnd('all计时开始');
    });
    
    console.time('race计时开始');
    Promise.race([myPromise1, myPromise2]).then((res) => {
        console.log('输出', res);
        console.timeEnd('race计时开始');
    });
    

    可以看到,all计时刚好1.5秒,race计时刚好1秒。

    我学到了什么

    1. 对Promise的理解更加深入。
    2. 对bind的使用更加熟练。
    3. 可以看到,Promise的缺点是对于每个异步方法,都需要用构造函数封装一遍,如果有其它需求,则需要更特别的封装。
    4. 打算找个时间用事件响应重新实现一遍,目前代码有点乱,如果用事件响应的话可能会更加优雅。
  • 相关阅读:
    透过WebGL 3D看动画Easing函数本质
    vmware虚拟机Windows 2003上网问题
    JAVA多态学习2
    h5播放音乐
    Unity3D:粒子系统Particle System
    文章标题
    【面试加分项】java自己定义注解之申明注解
    osgi实战学习之路:1. ant+bnd+felix搭建osgi之HelloWorld
    Android简单实现Socket通信,client连接server后,server向client发送文字数据
    句子开头
  • 原文地址:https://www.cnblogs.com/yangzhou33/p/9992588.html
Copyright © 2011-2022 走看看