Promise原理及手写Promise

原理:

Promise 原理围绕以下三个问题进行解决:

(有任何一步不了解的话请先往下看手写Promise,会一步步剖析原理,看完后再继续回顾这里!!)

 

1. 怎么实现异步?

Promise内部then函数注册(注意:不是执行)后续需要执行的函数,resolve函数执行。需要保证函数在执行前都已注册好,

所以resolve内部执行函数的代码需要加入延时机制setTimeout(0)放在任务队列的末尾

问题:如果Promise异步操作已经成功,这时,在异步操作成功之前注册的回调都会执行,但是在Promise异步操作成功这之后调用的then注册的回调就再也不会执行了=>

 

2. 怎么实现在Promise异步操作成功这之后调用的then注册的回调也会执行?

加入状态机制,若为pending,则将函数注册,等待后续resolve调用。若为fulfilled,则立即执行。resolve函数,将状态设为fulfilled

 

3. 怎么实现链式调用?

若是检测到resolve函数的参数newValue是一个promise对象(有then函数的对象),就使用对象冒充,

调用它的then函数(下一个函数newValue,和它的resolve)

 

手写Promise

1. 极简promise雏形

function Promise(fn) {
    var value = null,
        callbacks = [];  //callbacks为数组,因为可能同时有很多个回调

    this.then = function (onFulfilled) {
        callbacks.push(onFulfilled);
    };

    function resolve(value) {
        callbacks.forEach(function (callback) {
            callback(value);
        });
    }

    fn(resolve);
}
  1. 调用then方法,将想要在Promise异步操作成功时执行的回调放入callbacks队列,其实也就是注册回调函数,可以向观察者模式方向思考;
  2. 创建Promise实例时传入的函数会被赋予一个函数类型的参数,即resolve,它接收一个参数value,代表异步操作返回的结果,当一步操作执行成功后,用户会调用resolve方法,这时候其实真正执行的操作是将callbacks队列中的回调一一执行;

 

2. 加入延时机制

需要保证函数在执行前都已注册好,

实现:通过setTimeout机制,将resolve中执行回调的逻辑放置到JS任务队列末尾,以保证在resolve执行时,then方法的回调函数已经注册完成.(若不清楚为什么能放在js任务队列末尾,请查看js事件循环机制

=》 改造resolve函数

function resolve(value) {
    setTimeout(function() {
        callbacks.forEach(function (callback) {
            callback(value);
        });
    }, 0)
}

 

3. 加入状态机制

它总共有三种状态:pending(默认。等待状) fulfilled(成功状) rejected(失败状)

状态一旦改变,就不会改变

then函数中,若为pending,则将函数注册,等待后续resolve调用。若为fulfilled,则立即执行。resolve函数,将状态设为fulfilled

function Promise(fn) {
    var state = 'pending',
        value = null,
        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);
}

4. 实现链式Promise

所谓的链式Promise:

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

=> 实现:在then方法里面return一个promise就好啦。传递then方法生成的新promise的resolve方法,resolve(后邻promise),在resolve中,检测到参数是一个promise对象(有then函数的对象),就使用对象冒充,将改变后邻promise的resolve指向当前resolve(即将resolve传到后邻promise的then,并直接返回)

function Promise(fn) {
    var state = 'pending',
        value = null,
        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);
}

比如:来分析一个例子

getUserId()
    .then(getUserJobById)
    .then(function (job) {
        // 对job的处理
    });

function getUserJobById(id) {
    return new Promise(function (resolve) {
        http.get(baseUrl + id, function(job) {
            resolve(job);
        });
    });
}

1. then方法中,创建并返回了新的Promise实例,这是串行Promise的基础,并且支持链式调用。

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

3. getUserId生成的promise(简称getUserId promise)异步操作成功,执行其内部方法resolve,传入的参数正是异步操作的结果id

4. 调用handle方法处理callbacks队列中的回调:getUserJobById方法,生成新的promisegetUserJobById promise

5. 执行之前由getUserId promisethen方法生成的新promise(称为bridge promise)的resolve方法,传入参数为getUserJobById promise。这种情况下,会将该resolve方法传入getUserJobById promisethen方法中,并直接返回。

6. 在getUserJobById promise异步操作成功时,执行其callbacks中的回调:getUserId bridge promise中的resolve方法

7. 最后执行getUserId bridge promise的后邻promisecallbacks中的回调。

 

5.加入错误处理

function Promise(fn) {
    var state = 'pending',
        value = null,
        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;
        }

        var cb = state === 'fulfilled' ? callback.onFulfilled : callback.onRejected,
            ret;
        if (cb === null) {
            cb = state === 'fulfilled' ? callback.resolve : callback.reject;
            cb(value);
            return;
        }
        // 使用try-catch捕获错误
        try {
            ret = cb(value);
            callback.resolve(ret);
        } catch (e) {
            callback.reject(e);
        } 
    }

    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);
}

 

结束

如果文章对你有帮助,麻烦点赞哦!一起走码农花路~

 

  • 8
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值