资讯专栏INFORMATION COLUMN

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

gougoujiang / 657人阅读

摘要:宏任务和微任务这两个是指两个队列,脚本整体代码的回调及渲染都会被加入到队列中回调浏览器实现回调都会被加入到队列。

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)部分实现。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/83919.html

相关文章

  • Promise 规范解读实现细节 (二)

    摘要:开始前的实现原理已经在规范解读及实现细节一中说的很清楚了,这里将详细分析规范中的解析过程,最后会实现一个并提供用于测试的代码方法分析这里的调用会产生一个新的不同实例对于上一级的终值会作为的参数被传入对于如果返回一个返回一个那么的状态和值由决 开始前 Promise的实现原理已经在 Promise 规范解读及实现细节 (一) 中说的很清楚了,这里将详细分析 Promises/A+规范 中...

    Michael_Lin 评论0 收藏0
  • 理解 Promise 的工作原理

    摘要:前两个函数对应的两种状态和的回调函数。返回值是和对应的方法,但是会在下一事件循环返回。此外,在规范中,由方法生成的对象是已执行还是已拒绝,取决于由方法调用的那个回调是返回值还是抛出错误。但是对于其工作原理却有些懵懂和好奇。 原文: https://blog.coding.net/blog/how-do-promises-work Javascript 采用回调函数(callback)来...

    Achilles 评论0 收藏0
  • Promises A+规范原文解读 + es6实现(附详细注释)

    摘要:英文官方文档原文前言写本文的目的,是为了更好的理解,通过解读翻译原文,逐行解析原文通过代码一行一行实现。英中原因是一个值结果表明被拒绝的原因。英中在法律允许的范围内,组织已放弃所有版权及规范的相关或相邻权利。 英文官方文档原文:https://promisesaplus.com/ 前言 写本文的目的,是为了更好的理解promise,通过解读翻译原文,逐行解析原文通过代码一行一行实现。...

    v1 评论0 收藏0
  • 超详细的webpack原理解读

    摘要:生成文件,是模块构建的终点,包括输出文件与输出路径。这里配置了处理各模块的,包括预处理,编译,图片处理。各插件对象,在的事件流中执行对应的方法。修改改成引入模块在目录下执行, webpack原理解读 本文抄自《深入浅出webpack》,建议想学习原理的手打一遍,操作一遍,给别人讲一遍,然后就会了在阅读前希望您已有webpack相关的实践经验,不然读了也读不懂 本文阅读需要几分钟,理解需...

    wenhai.he 评论0 收藏0
  • 【全文】狼叔:如何正确的学习Node.js

    摘要:感谢大神的免费的计算机编程类中文书籍收录并推荐地址,以后在仓库里更新地址,声音版全文狼叔如何正确的学习简介现在,越来越多的科技公司和开发者开始使用开发各种应用。 说明 2017-12-14 我发了一篇文章《没用过Node.js,就别瞎逼逼》是因为有人在知乎上黑Node.js。那篇文章的反响还是相当不错的,甚至连著名的hax贺老都很认同,下班时读那篇文章,竟然坐车的还坐过站了。大家可以很...

    Edison 评论0 收藏0

发表评论

0条评论

gougoujiang

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<