拆开 JavaScript Promise 理解一波再组装起来

准备好材料、工具,开拆!!!

一个 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) {
                resolve(rejectHandle(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;
}, function (err) {
    console.log(‘rej ‘, err);
    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 

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

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

板块四、Promise/A+规范

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

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 MDN 及相关介绍文档中的案例为准,这里不一一赘述了... 这里简单的列出两个在使用 promise 过程中比较需要注意的点:

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

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

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

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. Promise/implementing

2. Promise 实现代码阅读

原文地址:https://www.cnblogs.com/ys-ys/p/9800971.html

时间: 2024-10-18 18:51:13

拆开 JavaScript Promise 理解一波再组装起来的相关文章

JavaScript Promise理解

定义 首先需要明确的是,Promise 是异步的.JS 异步操作是通过 JS 的事件循环机制 EventLoop 实现的. MDN Web Doc 对 Promise 的解释: Promise 对象是一个代理对象(代理一个值),被代理的值在 Promise 对象创建时可能是未知的.它允许你为异步操作的成功和失败分别绑定相应的处理方法(handlers). 这让异步方法可以像同步方法那样返回值,但并不是立即返回最终执行结果,而是一个能代表未来出现的结果的 Promise 对象. promise 对

【JavaScript】理解与使用Javascript中的回调函数

在Javascript中,函数是第一类对象,这意味着函数可以像对象一样按照第一类管理被使用.既然函数实际上是对象:它们能被“存储”在变量中,能作为函数参数被传递,能在函数中被创建,能从函数中返回. 因为函数是第一类对象,我们可以在Javascript使用回调函数.在下面的文章中,我们将学到关于回调函数的方方面面.回调函数可能是在Javascript中使用最多的函数式编程技巧,虽然在字面上看起来它们一直一小段Javascript或者jQuery代码,但是对于许多开发者来说它任然是一个谜.在阅读本文

javascript深入理解js闭包(看了挺多的,感觉这篇比较透彻)

闭包(closure)是Javascript语言的一个难点,也是它的特色,很多高级应用都要依靠闭包实现. 一.变量的作用域 要理解闭包,首先必须理解Javascript特殊的变量作用域. 变量的作用域无非就是两种:全局变量和局部变量. Javascript语言的特殊之处,就在于函数内部可以直接读取全局变量. Js代码 var n=999; function f1(){ alert(n); } f1(); // 999 另一方面,在函数外部自然无法读取函数内的局部变量. Js代码 function

[Javascript] Promise

Promise 代表着一个异步操作,这个异步操作现在尚未完成,但在将来某刻会被完成. Promise 有三种状态 pending : 初始的状态,尚未知道结果 fulfilled : 代表操作成功 rejected : 代表操作失败 如果 Promise 操作 fulfilled 或者 rejected ,并且对应的处理函数被声明了,则该处理函数被调用. Promise vs 事件监听器(event listener) 事件监听器善于处理同一对象上重复发生的事情,例如按键.点击鼠标等.对于这些事

javaScript Promise 入门

Promise是JavaScript的异步编程模式,为繁重的异步回调带来了福音. 一直以来,JavaScript处理异步都是以callback的方式,假设需要进行一个异步队列,执行起来如下: animate (ball1, 100, function () { animate (ball2, 200, function () { animate (ball3, 300, function () { animate (ball3, 150, function () { animate (ball2

JavaScript Promise异步实现章节的下载显示

Links: JavaScript Promise:简介 1.一章一章顺序地下载显示下载显示 使用Array.reduce()和Promise.resolve()将各章的下载及显示作为整体串联起来. [下载][显示]串联再串联. promise.resolve().[then().then()].[then().then()].... => 串联2.各章节分别下载完成后,才再一章一章显示 使用Array.map()将各章的下载并行起来,用Promise.all()将结果合并一处,然后再一章一章地

Javascript Promise 学习

Promise 就是处理异步的一个规范方法 a();b();alert("a");如果a() 里面有一个ajax 或者settimeout 那么alert("a") 会先跑这就是异步了.从前我们用一堆callBack函数来解决问题,但是这样写不好看.promise 的写法美丽多了依据上面的例子a().then(b).then(function(){alert("");})这样它会先跑完 a -> b - > alert("&

递归的再一次理解-斐波那契数列

链接:https://www.nowcoder.com/questionTerminal/c6c7742f5ba7442aada113136ddea0c3来源:牛客网 这个题可以说是迭代(Iteration) VS 递归(Recursion), f(n) = f(n-1) + f(n-2),第一眼看就是递归啊,简直完美的递归环境,递归肯定很爽,这样想着关键代码两三行就搞定了,注意这题的n是从0开始的: 1 2 if(n<=1) return n; else return Fibonacci(n-

JavaScript Promise启示录

本篇,主要普及promise的用法. 一直以来,JavaScript处理异步都是以callback的方式,在前端开发领域callback机制几乎深入人心.在设计API的时候,不管是浏览器厂商还是SDK开发商亦或是各种类库的作者,基本上都已经遵循着callback的套路. 近几年随着JavaScript开发模式的逐渐成熟,CommonJS规范顺势而生,其中就包括提出了Promise规范,Promise完全改变了js异步编程的写法,让异步编程变得十分的易于理解. 在callback的模型里边,我们假