博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
分步理解 Promise 的实现
阅读量:6047 次
发布时间:2019-06-20

本文共 10638 字,大约阅读时间需要 35 分钟。

一个 Promise 的运用:

var firstPromise = new Promise(function(resolve,reject){    setTimeout(function(){        var result = Math.random() <= 0.5 ? 1:0;        if(result){            resolve('resolved');        }else{            reject('rejected')        }    },1000)})var secondPromise = new Promise(function(resolve,reject){    setTimeout(function(){        var result = Math.random() <= 0.5 ? 1:0;        if(result){            resolve('resolved');        }else{            reject('rejected')        }    },2000)})firstPromise.then(function(value){     console.log(value);    return secondPromise;},function(reason){    console.log(reason);    return secondPromise;}).then(function(value){     console.log(value);},function(reason){    console.log(reason);})//  1s后随机输出结果 resolved 或者 rejected//  再1s后随机输出结果 resolved 或者 rejected

效果如上,在一个 promise 被完成/被拒绝时执行对应的回调取到异步结果。

同时,以上代码使用 promise 避免了回调地狱,规范了回调操作。

接下来,把 promise 拆成几块,学习一下怎么样的实现过程。

步骤一、Promise 构造函数

创建 promise 对象的构造函数,是创造 promise 的工厂。

基础要求:Promise 函数仅产生一个对象,避免大量变量的污染,将该藏好的对象/值藏好,该暴露的暴露;Promise 接收一个函数作为参数,且该函数在构造 promise 对象时被执行;Promise 必须有个 .then 方法(后续方法可自行扩展)。

function Promise(fn){
this.then = function(){ };}

步骤二、初始化过程,处理参数fn

Promise 构造函数参数 fn 中传入 resolve/reject;Promise 初始化的时候执行 fn 并在 promise 得到最终结果后执行传入的 resolve/reject ;resolve/reject 函数中执行 promise 中指定的完成/拒绝时回调函数,并以最终结果作为参数。

function Promise(fn){    // 完成时    function resolve(value) {        console.log('value ',value);    }    // 拒绝时    function reject(reason) {        console.log('reason ',reason);    }    // 执行传入的fn    function init(fn, onResolved, onRejected) {        try {            fn(function (value) {                onResolved(value);            }, function (reason) {                onRejected(reason);            })        } catch (err) {            onRejected(err);        }    }    init(fn, resolve, reject);        this.then = function(){ };}var promise = new Promise(function(resolve,reject){     setTimeout(function(){        var result = Math.random() <= 0.5 ? 1:0;        if(result){            resolve('resolved')         }else{            reject('rejected')        }    },1000) })// 1s后随机输出 value resolved 或者 reason rejected

步骤三、.then 里的处理流程

在promise中, .then 将传入的 resolvedHandle 和 rejectedHandle 函数存入 promise 的 handlers 中作为回调列表中的一项,在需要的时候(Promise被完成的时候)携带最终结果执行。

首先,假设有个异步操作,而且已经知道回调函数是什么,代码如下:

var resolvedHandle = function(res){ console.log(res) };var rejectedHandle = function(err){ console.log(err) };setTimeout(function(){    var result = Math.random() <= 0.5 ? 1:0;    if(result){        resolvedHandle('resolved');    }else{        rejectedHandle('rejected');    }},1000)// 1s后输出 resolved 或者 rejected

而对于 promise 而言,回调函数是在 .then 中传入并且在 promise 中给定义的,并且为了实现链式的操作, .then 中必须有返回一个对象,且对象须是一个携带 .then 方法的对象或函数或为一个 promise ,才足以继续执行.then。

// fn 作为初始化Promise时传入的函数,应该被立即执行并取出其中的调用function Promise(fn) {    var $resolveHandle = function (res) { };    var $rejectHandle = function (err) { };    // 执行Promise被完成时函数    function resolve(value) {        try {            var then = getThen(value);            if (then) {                init(then.bind(value), resolve, reject);                return;            };            fulfill(value);        } catch (err) {            reject(err);        }    }    // 完成时    function fulfill(value) {        $resolveHandle(value);        $resolveHandle = null;    }    // 拒绝时    function reject(reason) {        $rejectHandle(reason);        $rejectHandle = null;    }    // 执行传入的fn并执行回调    function init(fn, onResolved, onRejected) {        try {            fn(function (value) {                onResolved(value);            }, function (reason) {                onRejected(reason);            })        } catch (err) {            onRejected(err);        }    }    init(fn, resolve, reject);    function getThen(value) {        var t = typeof value;        if (value && (t === 'object' || t === 'function')) {            var then = value.then;            if (typeof then === 'function') {                return then;            }        }        return null;    };    this.then = function (resolveHandle, rejectHandle) {        return new Promise(function (resolve, reject) {            $resolveHandle = function (result) {                resolve(resolveHandle(result));            }            $rejectHandle = function (reason) {                if(rejectHandle){                    resolve(rejectHandle(reason));                }else{                    reject(reason)                }            }        })    }}var firstPromise = new Promise(function (resolve, reject) {    setTimeout(function () {        var result = Math.random() <= 0.5 ? 1 : 0;        if (result) {            resolve('resolved');        } else {            reject('rejected');        }    }, 1000);})var secondPromise = new Promise(function (resolve, reject) {    setTimeout(function () {        var result = Math.random() <= 0.5 ? 1 : 0;        if (result) {            resolve('resolved 2');        } else {            reject('rejected 2');        }    }, 2000);})firstPromise.then(function (res) {    console.log('res ', res);    return secondPromise;}).then(function (res) {    console.log('res 2 ', res);}, function (err) {    console.log('rej 2 ', err);})// 1s后随机输出 res resolved  或者  rej rejected// 又1s后输出 res 2 resolved 2 或者 rej 2 rejected 2 或者 rej 2 rejected

至此,上面的代码基本算是满足了一个 promise 的实现思路,但离正规军 promise 实现还存在一段距离

o(╥﹏╥)o...接下去学习吧。

步骤四、Promise/A+规范

由于 Promise/A+规范较长,就不放到文章里了,给链接吧(中午版是自己翻译的,有出入的地方还请以英文原版为准)

对照promise/A+规范,以上的Promise代码还存在问题:

  1.promise还需要存储promise状态和最终结果,以便后续被多次使用;

  2.同一个promise的.then方法中注册的回调函数可被多次执行,且回调函数可以是个列表;

  3.事件调度,回调函数应该在本轮.then方法所在事件队列结束后被调用;

  4.捕捉错误并做拒绝处理;

  更多细节...

继续改进,最后整改后的代码大致是这样的:

function Promise(fn) {    /* state    * 0 : pending     * 1 : resloved     * 2 : rejected     */    var state = 0;    var value = null;    var handlers = [];    function fulfill(result) {        state = 1;        value = result;        handlers.forEach(handle);        handlers = [];    };    function reject(error) {        state = 2;        value = error;        handlers.forEach(handle);        handlers = [];    };    function resolve(result) {        try {            var then = getThen(result);            if (then) {                init(then.bind(result), resolve, reject);                return;            }            fulfill(result);        } catch (err) {            reject(err);        }    };    function getThen(value) {        var type = typeof value;        if (value && (type === 'object' || type === 'function')) {            var then = value.then;            if (typeof then === 'function') {                return then;            }        }        return null;    };    function handle(handler) {        if (state === 0) {            handlers.push(handler);        } else {            if (typeof handler.onResolved === 'function') {                if (state === 1) {                    handler.onResolved(value);                };                if (state === 2) {                    handler.onRejected(value);                };            }        }    };    // 放到事件队列最后,在本轮事件执行完后执行    function timeHandle(callback, newValue) {        setTimeout(function () {            callback(newValue);        }, 0)    }    function init(fn, onResolved, onRejected) {        try {            fn(function (value) {                timeHandle(onResolved, value);            }, function (reason) {                timeHandle(onRejected, reason);            });        } catch (err) {            timeHandle(onRejected, err);        }    };    init(fn, resolve, reject);    this.then = function (onResolved, onRejected) {        if (!onResolved && !onRejected) {            throw new TypeError('One of onResolved or onRejected must be a function.')        };        return new Promise(function (resolve, reject) {            handle({                onResolved: function (result) {                    if (typeof onResolved === 'function') {                        try {                            resolve(onResolved(result));                        } catch (err) {                            reject(err);                        }                    } else {                        resolve(result);                    }                },                onRejected: function (error) {                    if (typeof onRejected === 'function') {                        try {                            resolve(onRejected(error));                        } catch (err) {                            reject(err);                        }                    } else {                        reject(error);                    }                }            })        })    };}var firstPromise = new Promise(function (resolve, reject) {    setTimeout(function () {        var result = Math.random() <= 0.5 ? 1 : 0;        if (result) {            resolve('resolved 1');        } else {            reject('rejected 1');        }    }, 1000);})var secondPromise = new Promise(function (resolve, reject) {    setTimeout(function () {        var result = Math.random() <= 0.5 ? 1 : 0;        if (result) {            resolve('resolved 2');        } else {            reject('rejected 2');        }    }, 3000);})firstPromise.then(function (res) {    console.log('res 1 ', res);    return secondPromise;}, function (err) {    console.log('rej 1 ', err);    return secondPromise;}).then(function (res) {    console.log('res 2 ', res);}, function (err) {    console.log('rej 2 ', err);})/* * * 1s后输出 res 1 resolved 1 或者 rej 1 rejected 1 * 2s后输出 res 2 resolved 2 或者 rej 2 rejected 2 * */

通过板块一、二、三的知识点,即可大致摸清promise的实现;板块四加上一些补充和限制,遵循一些规范,提高promise功能的可扩展性。

学会了怎么理解promise,更重要的是学会正确的使用它。

正确使用 Promise

promise 在业务开发中多用来处理异步或者多层回调的情况。

基础使用  及相关介绍文档中的案例为准,这里不一一赘述了... 这里简单的列出两个在使用 promise 过程中比较需要注意的点:

1. 不同平台环境下 Promise 的方法和遵循规则略微有些出入,详情以各平台环境下的 Promise 对象为基准。

  如 es6 Promise 存在Promise.race,Promise.all等方法,node中则没有这些方法。

  如 浏览器 Promise 事件调度走的是 setTimeout,node 走的是 process.nextTick 。(参考 [] )

2. Promise 虽可解决回调操作的规范问题(回调地狱),但也不能滥用 Promise (可能会占用过多内存)。

promise 解决后的结果会被存于内存中,被对应 promise 引用着,将上面的最终代码中测试的两个 promise 改成如下:

var firstPromise = new Promise(function (resolve, reject) {        setTimeout(function () {            var result = Math.random() <= 0.5 ? 1 : 0;            var str = '';            var i = 0, num = 500000;            for (; i < num; i++) {                str += 'promise '            }            if (result) {                resolve('resolved 1 : ' + str);            } else {                reject('rejected 1 : ' + str);            }        }, 1000);    })

则内存占用情况如下:

这些是一些平台差异或业务需求方面的不同点,对 Promise 核心实现并影响甚微,对 Promise 扩展方法有影响,对业务中 Promise 的使用有影响。

参考

1.  

2. 

 

转载地址:http://upxex.baihongyu.com/

你可能感兴趣的文章
java 8 lambda表达式
查看>>
如何设置linux支持上传的文件中文不乱吗
查看>>
Python3.4+Django1.9+Bootstrap3
查看>>
LoadRunner
查看>>
django test基本操作
查看>>
部分端口号
查看>>
metamodel
查看>>
MariaDB的双主
查看>>
紫书第一章训练1 D -Message Decoding
查看>>
从技术人视角看闪电网络之闪电路由
查看>>
屏蔽F1~F12的快捷键的js函数
查看>>
取右端4到7位
查看>>
[转载]Spring Cloud初探
查看>>
Mysql百万级数据查询优化
查看>>
关于laravel框架分页报错的问题
查看>>
day22 collection 模块 (顺便对比queue也学习了一下队列)
查看>>
day27 多态 多继承 接口类 抽象类
查看>>
Centos Samba 服务器 iptables 和 SElinux 设置
查看>>
Redis sentinel之集群搭建
查看>>
hdu分类
查看>>