JavaScript --手写Promise源码

news/2024/7/11 0:45:13 标签: javascript, es6, promise, 手写promise, 异步编程

手写Promise

先来捋一下我们使用正常promise的的场景,然后去实现MyPromise这个类相对应的功能。

1.实现核心逻辑 类,构造函数执行器,状态,resolve,reject函数改变状态,值,then方法定义状态改变后的回调等功能。

2.基础类 的基础上加入异步逻辑,就是说执行器函数里加入延时操作,异步任务后更改promise对象的状态

3.实现使用then方法多次被调用,添加多个处理的回调函数。

4.then方法的链式调用:

  1. .then方法返回一个promise对象,这个promise对象在then方法里面可以拿到then方法里面回调函数的返回值,然后把这个返回值作为新promise对象的返回值输出,在被链式调用的时候就可以被它的then方法的回调函数拿到
  2. .同时还要判断这个then方法回调函数的返回值是普通值还是promise对象,是promise对象的话还需要在返回值promise对象的then方法的回调里面 使用新promise的resolve或reject把值传出去。

5.判断第一个then方法的回调函数返回值不能是promise2本身,否则就会发生promise对象的循环调用,我测试了很久发现这个promise2对象的状态将永远为pedding,永远不会被改变! 

6-1.   之前我们还没有在Mypromise的类当中去进行任何的错误处理。接下来回想一下在使用Promise时我们所有可能出现错误或者抛出异常的情况:

  1. 执行器函数:执行器当中的代码在执行中发生错误时,执行reject()函数。
  2. then方法:then方法的回调函数在执行过程中报错,在下一个then方法的错误回调中捕获。

6-2. then方法链式调用的时候,只处理了成功的状态,还要处理rejected状态和pedding状态。

7.then方法的参数变成可选参数:then方法可以不传参数,promise状态就会一直向后传递,直到传递给有回调函数的then方法。

8. Proise.all 特点  :

  1.    返回一个Promise
  2.   入参是数组 resolve的情况下出参也是数组 且结果顺序和调用顺序一致
  3.    所有的值或者promise都完成才能resolve 所有要计数
  4.   只要有一个为reject 返回的Promise便reject

9.Promise.resolve的方法实现:把给定的值 转化为promise对象  1.普通值,2.promise对象 直接返回。

写源码过程中烧脑的问题1:链式调用的时候,如果promise对象then方法的回调函数的返回值的是then方法返回的promise对象,那么这个promise对象的状态将永远为pedding,永远不会被改变!  所以需要判断then方法的回调函数返回值不能为then方法返回的promise对象。需要判断的这个操作。

下面上代码:

javascript">// 这里定义三个常量,分别是promise对象的三种状态 pedding, fulfilled , rejected
const PEDDING = "pedding";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";
class MyPromise{
    // promise对象是通过new Promise生成的一个实例化对象,
    // 所以我们要写的MyPromise是一个类,它接收一个参数,这个参数是一个函数,这个函数被称为Promise的执行器,这个执行器立即执行    
    constructor(executor){
        // 执行器执行的时候 接收两个参数resolve和reject,这两个参数也是两个函数,它们的作用是改变promise对象的状态并传值给then方法的回调函数
        // resolve将promise对象的状态修改为fulfilled成功状态,reject将promise对象的状态修改为rejected失败状态
        try{
            executor( this.resolve,this.reject )
        } catch(e){
            this.reject(e.message)
        }
        
    }
    // 定义promise对象的默认状态为pedding
    status = PEDDING;
    // 成功之后的值
    value = undefined;
    // 失败之后的值
    reason = undefined;
    successCallback = [];
    failCallback = [];
    //  定义resolve函数,它接收数据并把
    resolve = (value)=>{
        if(this.status!=PEDDING) return;        //一旦状态确定就不可更改
        this.status = FULFILLED;
        this.value = value;
        // 如果this.successCallback的长度大于0,说明在promise对象状态为pedding的时候,就已经在promise对象的then方法定义过状态改变的回调函数了,那就执行吧!
        // 下面reject函数中同理
        if(this.successCallback.length>0){
            this.successCallback.forEach((Callback)=>{
                Callback(this.value)
            })
        }
    }
    //  定义reject函数
    reject = (reason)=>{
        if(this.status!=PEDDING) return;        //一旦状态确定就不可更改
        this.status = REJECTED;
        this.reason = reason;        
        
        if(this.failCallback.length>0){
            this.failCallback.forEach((item)=>{
                item(this.reason)
            })
        }
    }
    // then方法 接收两个函数作为参数,这两个函数会被定义为promise状态更改后需要执行的回调函数
    then(successCallback,failCallback){
        successCallback = successCallback ? successCallback : value=>value;
        failCallback = failCallback ? failCallback : reason=>  {throw reason};
        // console.log(successCallback,failCallback)
        const promise2 = new MyPromise((resolve,reject)=>{
            // 当前状态为成功
            if(this.status == FULFILLED){
                setTimeout(()=>{
                    try{
                        let x = successCallback(this.value);
                        // console.log(x)
                        resolvePromise(promise2,x,resolve,reject)
                    }catch(e){
                        reject(e.message)
                    }
                    
                },0);
                                
            }else if(this.status == REJECTED){
            // 当前状态为失败
                setTimeout(()=>{
                    try{
                        let x = failCallback(this.reason)
                        resolvePromise(promise2,x,resolve,reject)
                    }catch(e){
                        reject(e)
                    }
                    
                },0);
            }else{
            // 当前状态为pedding(进行中!)
                // 往this.successCallback中添加回调函数可以实现使用then方法多次被调用                
                this.successCallback.push(
                    ()=>{
                        setTimeout(()=>{
                            try{
                                let x = successCallback(this.value)
                                resolvePromise(promise2,x,resolve,reject)
                            }catch(e){
                                reject(e.message)
                            }
                        },0);
                    }
                );
                this.failCallback.push(
                    ()=>{
                        setTimeout(()=>{
                            try{
                                let x = failCallback(this.reason)
                                resolvePromise(promise2,x,resolve,reject)
                            }catch(e){
                                reject(e.message)
                            }
                        },0);
                    }
                );              
            }
        })
        return promise2;
    }

    // catch方法就是then方法的一个别名,它专门用来捕获错误,看起来只传递了一个回调函数,实际上把它的回调函数传给了then方法的第二个参数
    catch(callBack){
        return this.then(undefined,callBack);
    }

    // finally方法的作用:
    // 1.是无论当前promise对象最终成功还是失败都会调用finally方法,并且它还可以链式调用then方法来拿到当前promise对象的值
    // 2.finally方法的返回值如果是promise2对象,那么它链式调用的then方法会等待它返回值的promise2对象执行完成,但它链式调用的then方法接收到的值是当前promise对象的值
    finally(Callback){
        // return MyPromise.resolve( Callback() ).then()
        return this.then((value)=>{            
            let end = MyPromise.resolve( Callback() ).then(()=>{return value});
            // console.log(end);
            return end;           
        },reason=> {            
            return MyPromise.resolve( Callback() ).then(()=>{throw reason})
        });
    }

    static all(array){
        let result = [];
        let num = 0;    //计数器
        return new MyPromise((resolve,reject)=>{
            
            function addResult(index,value){
                num++;
                result[index] = value;
                // 当计数器等于传入数组的长度时,表明传入的所有promise对象或值都已经执行完毕,可以将当前promise对象状态改变为成功,并返回所有值。
                if(num == array.length){
                    resolve(result);
                }
            }
            for(let i=0;i<array.length;i++){
                if(array[i] instanceof MyPromise){
                    // 传入的数组元素执行成功就往result里面添加,有一个失败则将当前promise对象状态改变为失败,并返回失败原因。
                    array[i].then((value)=>{addResult(i,value)} , (reason)=>{ reject(reason) })
                }else{
                    addResult(i,array[i])
                }
            }
        })        
    }

    static resolve(x){
        if( x instanceof MyPromise ){
            return x;
        }else{
            return new MyPromise((resolve,reject)=>{
                resolve(x);
            })            
        }
    }
}

// 封装一个方法判断第一个then方法的回调函数返回值是普通值还是promise对象,拿到最终值 改变promise2的状态并把值传出去
function resolvePromise(promise2,x,resolve,reject){
    if( x == promise2 ) { 
        // 判断第一个then方法的回调函数返回值不能是promise2本身,否则就会发生promise对象的循环调用,我测试了很久发现这个promise2对象的状态将永远为pedding,永远不会被改变! 
        // 也就是链式调用在此步断掉了,所以必须做这个判断
        return reject( new TypeError("chaining circle are detected for promise #<Promise>") ) 
    }
    if( x instanceof MyPromise ){
        // x是promise
        x.then( (value)=>resolve(value),(reason)=>reject(reason) )
    }else{
        // x是普通值
        resolve(x);
    }
}
module.exports = MyPromise;

 

 


http://www.niftyadmin.cn/n/1496639.html

相关文章

python 解码_python的编码和解码

编码介绍:1. ASCII: 英文, 特殊字符, 数字, 8bit, 1byte2. GBK: 中文 16bit, 2byte. 兼容ASCII3. unicode: 万国码, 32bit 4byte. 兼容ASCII4. UTF-8: 长度可变的unicode. 英文:8bit, 欧洲:&#xff11;&#xff16;bit, 中文:24bit 3bytepython2 只能用ASCIIpython3 有unicod…

云服务器启动tomcat巨慢,很慢

增加随机数生成熵池 0、查看熵池 cat /proc/sys/kernel/random/entropy_avail1、 yum install rng-tools2、 systemctl start rngd3、 重复查看熵池 正文&#xff1a; 自己再阿里云申请了一台1G1核的机器&#xff0c;每次重启自己的服务tomcat都需要卡住很长时间&#xff0c;每…

ECMAScript新特性(一)

1. ECMAScript 与 JavaScript 的关系&#xff1f; ES 通常可以看作是 JavaScript 的标准化语言规范。但实际上JavaScrpit 是ECMAScript的扩展语言。 在ECMAScript中只是提供了最基本的语法&#xff0c;通俗点说就是约束了我们的代码该如何编写&#xff0c;例如&#xff1a;如…

mysql数据库函数的用法_MySQL数据库8——数据库中函数的应用详解

数据库中内置函数的使用该篇主要介绍数据库中内置函数的使用&#xff0c;主要有日期函数&#xff0c;字符串函数&#xff0c;数学函数。(一)日期函数select current_date();//获得当前日期&#xff0c;年月日select current_time();//获得当前时间&#xff0c;时分秒select cur…

ECMAScript新特性(二)

14. ES2015 Promise ES2015 新出的一种更优的异步编程解决方案&#xff0c;解决了传统异步编程中回调函数嵌套过深的问题。具体可看我的这篇文章&#xff1a;JavaScript 异步编程-------Promise&#xff0c;或你对 Promise 内部实现原理感兴趣&#xff0c;也可以查看这篇文章&…

JavaScript 类型系统

TypeScript 是一门基于JavaScript之上的一门语言&#xff0c;它重点解决了JavaScript语言类型系统的不足。使用TypeScript可以大大提高代码的可靠程度。 接下来学习的不止是TypeScript这个语言&#xff0c;因为我们要学习的目标是JavaScript语言自有类型系统的问题&#xff0c…

mysql 版本兼容_不同版本mysql语句不兼容原因

一般是sql_mode不相同&#xff0c;可以认为规则不一致。(语法的变化非常小&#xff0c;一般可以忽略)如果想要导入不同版本的数据。可以&#xff1a;手动处理一些导入错误或者采用其他的办法。或者修改sql_mode。具体修改方法参考http://www.mysqlfaqs.net/mysql-client-server…

安装express遇到的坑(express命令不起作用)

express命令失效 描述&#xff1a; 我们有时候会遇到全局安装express的时候&#xff08;eg. npm install -g express&#xff09;&#xff0c;使用express命令失效,有如下两种原因。 原因1&#xff1a; 你安装的express是4.0以上的版本&#xff0c;express在4.0之后&#xff0c…