likes
comments
collection
share

痛下心来整理一下Promise

作者站长头像
站长
· 阅读数 10

Promise会用,说原理吧,好像懂好像有不懂,还是记录自己整理的思路吧,算是面试复习

以下场景是我们最常用的一个异步请求

function getUserId() {  
    return new Promise(function (resolve) {    
        //异步请求    
        http.get(url, function (results) {      
            resolve(results.id);    
        });  
    });
}
getUserId().then(function (id) {  
    //一些处理
});

分析特点

1、创建Promise实例时传入的函数会被赋予一个函数类型的参数,即resolve,它接收一个参数value,代表异步操作返回的结果,当一步操作执行成功后,用户会调用resolve方法;

2、调用then方法,参数也是一个(暂且只考虑成功)方法,该方法的参数是我们想要的结果;

resolve 的的参数,是 then 参数函数的参数,这样我们可以考虑把 then 的参数函数在promise 中做暂存,然后再resolve 中取出执行,考虑是异步,且我们了解 then 可以多次链式调用,返回的是一个 Promise,然后我们知道 promise 的状态一旦确定就不会在逆转,那么我们考虑可能是这样的情况

function Promise (fn) {  
    let value = null;  
    const callbacks = [];  
    this.then = function (onFulfilled) {    
        callbacks.push(onFulfilled);    
        return this;  
    };  
    function resolve (value) {    
        setTimeout(function () {      
            callbacks.forEach(function (callback) {        
                callback(value);      
            });    
        }, 0)  
    }  
    fn(resolve);
}

这样好像还存在一个问题,如果Promise异步操作已经成功,这时,在异步操作成功之前注册的回调都会执行,但是在Promise异步操作成功这之后调用的then注册的回调就再也不会执行了,这显然不是我们想要的。

我们可以在 then 中做一个判断如果是 pending 则执行注册逻辑,否则直接执行回调,是不是可以解决这个问题

加入状态

function Promise (fn) {  
    let state = 'pending'  
    let value = null  
    const callbacks = []  
    this.then = function (onFulfilled) {    
        if (state === 'pending') {      
            callbacks.push(onFulfilled);      
            return this;    
        }    
        onFulfilled(value);    
        return this;  
    };  
    function resolve (newValue) {    
        value = newValue;    
        state = 'fulfilled';    
        setTimeout(function () {      
            callbacks.forEach(function (callback) {        
                callback(value);      
            });    
        }, 0);  
    }  
    fn(resolve);
}

链式Promise

链式Promise是指在当前promise达到fulfilled状态后,即开始进行下一个promise(后邻promise)。那么我们如何衔接当前promise和后邻promise呢?

我们可以先在 then  返回一个 promise 看看效果

function Promise (fn) {  
    let state = 'pending'  
    let value = null  
    const callbacks = []  
    this.then = function (onFulfilled) {    
        return new Promise(function (resolve) {      
            handle({        
                onFulfilled: onFulfilled || null,        
                resolve: resolve      
            });    
        });  
    };  
    function handle (callback) {    
        if (state === 'pending') {      
            callbacks.push(callback);      
            return;    
        }    
        //如果then中没有传递任何东西    
        if (!callback.onFulfilled) {      
            callback.resolve(value);      
            return;    
        }    
        var ret = callback.onFulfilled(value);    
        callback.resolve(ret);  
    }  
    function resolve (newValue) {    
        if (newValue && (typeof newValue === 'object' 
            || typeof newValue === 'function')) {      
            var then = newValue.then;      
            if (typeof then === 'function') {        
                then.call(newValue, resolve);        
                return;      
            }    
        }    
        state = 'fulfilled';    
        value = newValue;      
        setTimeout(function () {      
            callbacks.forEach(function (callback) {        
                handle(callback);      
            });    
        }, 0);  
    }  
    fn(resolve);
}

注意

handle方法是promise内部的方法。then方法传入的形参onFulfilled以及创建新Promise实例时传入的resolve均被push到当前promisecallbacks队列中,这是衔接当前promise和后邻promise的关键所在(这里一定要好好的分析下handle的作用)。

示例

getUserId()
    .then(getUserJobById)  
    .then(function (job) {    
        // 对job的处理  
    });
function getUserJobById (id) {  
    return new Promise(function (resolve) {    
        http.get(baseUrl + id, function (job) {      
            resolve(job);    
        });  
    });
}

这里比较绕,我大概梳理一下,首先 getUserId  直接执行并返回了 promise对象(Ap)。在Ap then 的过程把 getUserJobById 作为回调注册,然后  Ap 的 reslove 执行,即  getUserJobById 执行,这样 Ap resolve 的 value 作为入参 传入 getUserJobById  生产返回新的 promise(Bp),接下来是就是操作 Bp 的的过程了。

then 的过程是返回了新的 promise ,至于如何保证顺序我觉得是 父级回调的注册执行生成子级 promise,行成了闭包。

接下来我们在加上 reject 的状态管理

function Promise (fn) {  
    let state = 'pending';  
    let value = null;  
    const callbacks = [];  
    this.then = function (onFulfilled, onRejected) {    
        return new Promise(function (resolve, reject) {      
            handle({        
                onFulfilled: onFulfilled || null,        
                onRejected: onRejected || null,        
                resolve: resolve,        
                reject: reject,      
            });    
        });  
    };  
    function handle (callback) {    
        if (state === 'pending') {      
            callbacks.push(callback);      
            return;    
        }    
        let cb = state === 'fulfilled' ? callback.onFulfilled : callback.onRejected,      
        if (cb === null) {      
            cb = state === 'fulfilled' ? callback.resolve : callback.reject;      
            cb(value);      
            return;    
        }    
        let ret = cb(value);    
        callback.resolve(ret);  
    }  
    function resolve (newValue) {    
        if ( newValue && 
            (typeof newValue === 'object' || typeof newValue === 'function')) {
              var then = newValue.then;      
              if (typeof then === 'function') {        
                    then.call(newValue, resolve, reject);        
                    return;      
              }    
        }    
        state = 'fulfilled';    
        value = newValue;    
        execute();  
    }  
    function reject (reason) {    
        state = 'rejected';    
        value = reason;    
        execute();  
    }  
    function execute () {        
        setTimeout(function () {      
            callbacks.forEach(function (callback) {        
                handle(callback);      
            });    
        }, 0);  
    }  
    fn(resolve, reject);
}