Promise 规范解读及实现细节 (一)

1. macrotask (宏任务)和 microtask (微任务)

这两个是指两个队列,脚本整体代码、setTimeout、setInterval、setImmediate、I/O、的回调及UI渲染都会被加入到 macrotask 队列中, process.nextTick回调、Promise(浏览器实现)、MutationObserver回调都会被加入到microtask队列。

  • 1 当浏览器进入事件循环时,先去看macrotask中有没有具备执行条件的任务
  • 2 如果有则选取一个(只选一个)执行
  • 3 没有则去看microtask队列有没有具备执行条件的任务
  • 4 有则全部执行(全部执行)
  • 5 去更新页面
  • 6 然后重复以上行为

2. Promise 介绍

Promise主要的功能是将异步执行结果保存起来,从而使得我们不再用回调的方式去处理异步任务的结果,我们通过一个回调将异步任务的结果保存起来,在以后某个时刻通过各种姿势去使用它new romise(excutor).then(function(result)}).then(function(result){});的方式去执行异步任务,Promise 是通过回调来实现的,但它将回调统一,使得我们不在去直接接触回调,用看似有点像同步的方式去写异步任务,也就仅仅是这样而已,并没有什么新东西,这种同步方式只能说是类似同步,下一篇会讲写法更接近同步、更加有建术的 generator 。

3. Promise 的构造函数应该是这样的

function Promise(excutor) {
    var self = this;
    var status = 'PENDING';  //Promise的当前状态
    var data = undefined;  //当前excutor(就是用户的异步任务)的执行结果
    self.onResolvedCallback = [];  //excutor 执行成功后回调函数队列
    self.onRejectedCallback = [];  //excutor 执行失败后回调函数队列

    function resolve(value) {  
        // TUDO 异步任务执行成功后的行为
        // 1 改变当前promise的status值 调用该方法 PENDING->RESOLVED
        // 2 执行onResolvedCallback队列里面的所有回调函数并传递异步任务的异步处理结果
        // onResolvedCallback[i](data)
    }

    function reject(value) {
        // TUDO 异步任务执行失败后的行为
        // 1 改变当前promise的status值 调用该方法 PENDING->REJECTED
        // 2 执行onRejectedCallback队列里面的所有回调函数并传递异步任务的异步处理结果
        // onRejectedCallback[i](data)
    }

    excutor(resolve, reject);  //执行用户添加的任务
}

Promise.prototype.then = function (onResolved, onRejected) {
    // onResolved异步任务执行成功的回调
    // onRejected异步任务执行失败后的回调
}

看起来就是这样的 具体使用方式
new Promise(function(resolve,reject){
    setTimeout(function(result){
        // resolve(result) 或reject(result) 根据返回值决定
        //这里我们确实还是写了回调,但是我们在回调里面没有做太多的事件
        //仅仅是传递了异步处理结果 想想如果,我们在这里又要做依赖于result的
        //异步任务会怎样,回调地狱,代码结构混乱,不容易读
    });
}).then(function(result){}) //这里的result是从上面的那个回调里面传递的,是同一个值

4. 具体实现

(1) resolve 函数和 reject 函数

function Promise(excutor) {
    var self = this;
    var status = 'PENDING';  //Promise的当前状态
    var data = undefined;  //当前excutor(就是用户的异步任务)的执行结果
    self.onResolvedCallback = [];  //excutor 执行成功后回调函数队列
    self.onRejectedCallback = [];  //excutor 执行失败后回调函数队列

    function resolve(value) {  
        // TUDO 异步任务执行成功后的行为
        if(self.status === 'PENDING') {
            self.status = 'REJECTED'; // 改变当前promise的status值 调用该方法 PENDING->RESOLVED
            self.data = value;  // 更新当前的data
            for(var i in self.onResolvedCallback) {
                self.onResolvedCallback[i](self.data);  //执行onResolvedCallback队列里面的所有回调函数并传递异步任务的异步处理结果
            }
        }
    }

    function reject(reson) {
        // TUDO 异步任务执行失败后的行为
        if(self.status === 'PENDING') {
            self.status = 'REJECTED';
            self.data = reson;
            for(var i in self.onRejectedCallback) {
                self.onRejectedCallback[i](self.data);
            }
        }
    }
    try {
        excutor(resolve.bind(this), reject.bind(this));  //执行用户添加的任务
        /*这里为什么要通过通过 bind 来绑定上下文呢,主要是 resolve 依赖当
        前 promise的内部属性,在excutor函数体内,我们是通过 resolve(data)
        或者reject(reson) 的方式来调用通过这种方式调用 在非严格模式
        resolve或reject的上下文是windows,在严格模式下是undefined*/
    } catch(e) {
        reject.bind(this)(e);  //出现异常则通过reject向后传递
    }
}

(2) then 方法的实现

then方法应当具备如下功能
(1) then方法必须返回一个Promise对象,也就是,对应用户添加的逻辑要进行包装
(2) 同一个对象可多次调用then来添加逻辑,并且会按照添加顺序执行

//例如:

var promise = new Promise(function(resolve, reject) {
    //异步操作 并且在回调中调用了 resolve或者 reject将异步处理结果传递出去
});
promise.then(function(data){
    //对异步结果data进行操作
});
promise.then(function(data){
    //对异步结果data进行操作
});
//promise是同一个对象

(3) 可以链式调用

//例如:
new Promise(function(resolve, reject){
    //异步操作 并且在回调中调用了 resolve或者 reject将异步处理结果传递出去
}).then(function(data){}).then(function(data){});

这里第一次和第二次调用then的对象是两个不同的对象,这里要注意

[1] 如果第一个then的参数也是一个Promise那么在第二个then的中会得到第一个then的处理结果
[2] 如果第一个then的参数是一个函数并且有返回值,则在第二个then中会接收到这个返回值
[3] 其它情况下都会得到通过 new Promise(fn) 这里这个异步fn的处理结果

(4) 我们写的Promise 要和所有符合Promise规范的 Promise 能协同工作(例如浏览器自带的、第三方库的)

/**
*@parms onResolved 任务成功时执行的回调
*@parms onRejected 任务失败时执行的回调
*/
Promise.prototype.then = function(onResolved, onRejected) {
    var self = this;
    var promiseThen;  //then方法返回的promise

    //如果 onResolved或onRejected 不是函数我们忽略它,并且添加一个用于传递异步结果得函数
    onResolved = typeof onResolved ==='function' ? onResolved : function(value) {resolve(value);}
    onRejected = typeof onRejected ==='function' ? onRejected : function(reson) {resolve(reson);}

    /*如果在调用then添加依赖于异步处理结果的时候,异步任务已经执行完了,那么
    用异步的结果执行then里面添加的任务*/
    if(self.status === 'RESOLVED') {
        //这里要将then里面要添加的任务包装成一个Promise,返回Promise 是为了(3)中的链式调用
        return promiseThen = new Promise(function(resolve, reject) {
            try {
                //执行then添加的任务
                var x = onResolved(self.data);
                if(x instanceof Promise) {
                    /*如果then添加的任务有返回值,并且返回值是Promise对象
                    则让promiseThen去接受x的状态和data值注意,这里将
                    promiseThen的resolve,和reject作为x的then参数传入,这
                    样当promiseThen的resolve和reject会在适当的时候被调用
                    从而使得promiseThen接受了x的状态和data值Promise的不
                    同对象之间能实现相互影响,是通过将自己的resolve和
                    reject添加到其它对象中*/
                    x.then(resolve, reject); 
                }
                else resolve(x); //如果x不是Promise使用x的值作为promiseThen的结果
            } catch (e) {
                reject(e);
            }
        });
    }

    //和上面逻辑相同
    if(self.status === 'REJECTED') {
        return promiseThen = new Promise(function(resolve, reject){
            try {
                var x = onRejected(self.data);
                if(x instanceof Promise){
                    x.then(resolve, reject);
                }
            }catch(e) {
                reject(e);
            }
        });
    }

    if(self.status === 'PENDING') {
        //当前Promise的异步任务没有执行完,则将then里面的异步任务 包装后放入队列
        //包装方法和上面一样,区别是上面立即执行,这里放入回调队列。
        return promiseThne = new Promise(function() {
            self.onResolvedCallback.push(function(){
                try {
                    var x = onResolved(self.data);
                    if(x instanceof Promise) {
                        x.then(resolve, reject);
                    } catch(e) {
                        reject(e);
                    }
                }
            });

            self.onRejectedCallback.push(function(){
                try {
                    var x = onRejected(self.data);
                    if(x instanceof Promise) {
                        x.then(resolve, reject);
                    } catch(e) {
                        reject(e);
                    }
                } 
            });
        });
    }

}

写到这里Promise具体实现的 (1)、(2)、(3)的功能已经完成了,也就是如果不考虑与其它
的Promise交互那么Promise的原理已经说清楚了,本来打算一篇文章写完,但是篇幅太
长,所以打算分两篇文章来写,下一篇是generator,之后我会按照规范将(4)部分实现。

    原文作者:_ivenj
    原文地址: https://segmentfault.com/a/1190000010106482
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞