JavaScript 进阶 41 -- Promise 手写简单的源码

本文介绍了Promise的基本使用,并尝试手写Promise的简单源码,探讨了Promise的状态转换、then方法的逻辑以及链式调用的实现原理。通过实例解析,帮助读者深入理解Promise在JavaScript中的异步控制。

前面的一篇说了说Promise的基本用法和常用的方法。知道了Promise的实现原理,我来自己试着谢谢源码

先看看promise是怎么用的

【 例 】

let p = new Promise((resolve,reject)=>{
    //同步代码先执行,异步代码先放着,此时是等待状态
    console.log('先执行同步的')
    setTimeout(() => {
        resolve('异步执行成功'); 
    }, 1000);
    
})
p.then((value)=>{
    //成功时
    console.log('恭喜',value);//恭喜 成功了
},(reason)=>{
    //失败时
    console.log('失败了',reason);//失败了 不努力
})

先来实现上面这段代码吧 ,对照上面此代码 写出原生promise 文件:Promise.js,先来说说它的思路吧:

调用resolve时 走成功回调函数,调用reject时 走失败回调函数, resolve 和 reject 这两个函数改变的是promise的状态,只能 从 pending ---->fulfilled 或者 pending---->rejected,不能成功态转为失败态,或者失败态转成功态。在状态改变的同时将结果传递出去,resolve()传递成功的数据,reject() 传递失败的数据。then() 方法接收两个参数,一个是 调resolve时传递成功的值,一个是调用reject时传递的失败的数据。根据已经发生改变的状态决定执行哪个回调

【 超级简洁版 -1 】文件名:Promise.js 。测试这个文件是否正确呢 ,

//定义三种状态
const PENDING = 'PENDING';
const RESOLVE = 'RESOLVE';
const REJECT = 'REJECT';

//Promise是个类
class Promise {
    constructor(executor){

        //初始状态---静待状态
        this.status = PENDING;

        this.value = undefined;//记录成功的值
        this.reason = undefined;//记录失败的原因

        //这是异步的  先执行同步代码,异步的处于等待状态 发布订阅模式--->订报纸,报纸发放到信箱,看报纸时从信箱取出报纸
        this.resolveCallbacks = [];//先保存起来,待执行时从数组中取出 相当于信箱
        this.rejectCallbacks = []; // 发报纸放到信箱,看报纸时从信箱取出来

        let resolve = (value)=>{
            if(this.status === PENDING){
                this.status = RESOLVE;
                this.value = value;
                // 发布(收到报纸以后)将来调用成功时将值取出来
                this.resolveCallbacks.forEach(fn=>fn(this.value))
            }
        }
        let reject = (reason)=>{
            if(this.status === PENDING){
                this.status = REJECT;
                this.reason = reason;
                this.rejectCallbacks.forEach(fn=>fn(this.reason))
            }
        }
        //如果代码出错了就抛出异常,走reject并捕获错误
        try{
            executor(resolve,reject);
        }catch(e){
            reject(e);
        }
        
    }
    //resolve,reject 不是实例上自带的方法 ,是自己定义法的方法
    //每个实例都有then方法,是原型上的
    then(onfulfilled,onrejected){
        if(this.status === RESOLVE){
            onfulfilled(this.value)
        }
        if(this.status === REJECT){
            onrejected(this.reason)
        }
        //等待状态 执行时 可能成功 可能失败
        if(this.status === PENDING){
            //将成功和失败 回到保存起来  订阅(订报纸)
            this.resolveCallbacks.push(onfulfilled);
            this.rejectCallbacks.push(onrejected);
        }
    }
}

//导出
module.exports = Promise;

还是上面的例子,在代码的最上面 引入我们刚刚写的这个文件  let Promise = require('./Promise')

//引入自己写的原生promise.js 文件
let Promise = require('./Promise');

let p = new Promise((resolve,reject)=>{
     resolve("成功了");
    //reject('不努力');
    //throw new Error('出错喽');
})
p.then((value)=>{
    //成功时
    console.log('恭喜',value);//恭喜 成功了
},(reason)=>{
    //失败时
    console.log('失败了',reason);//失败了 不努力
})

异步代码(有个模式 叫 发布订阅)

当调用then时 异步代码处于等待状态 还不知道是成功还是失败,先将成功后的执行代码和失败后要执行的代码 保存起来, (相当于订阅)--》订一份报纸 ,放到报箱里,然后在取出来看报纸。当调用resolve的时候,再把它取出来执行

【 简洁版 例 2 】该段的Promise源代码:实现了下面两点逻辑  这个问题可参考我的前一篇文章

1、上一次then的某个回调函数是普通值,会执行下一次then的成功回调(上一次then的某个回调函数是失败态,会返回undefined,则下次的then会走成功态)

2、上一次then的某个回调函数抛异常,会执行下一次then的失败回调

const PENDING = 'PENDING';
const RESOLVE = 'RESOLVE';
const REJECT = 'REJECT';

class Promise{
    constructor(executor){

        this.status = PENDING;

        this.value = undefined;
        this.reason = undefined;
        //
        this.resolveCallBacks = [];
        this.rejectCallBacks = [];


        let resolve = (value)=>{
            if(this.status === PENDING){
                this.status = RESOLVE;
                this.value = value;
                this.resolveCallBacks.forEach(fn=>fn())
            }
        }
        let reject = (reason)=>{
            if(this.status === PENDING){
                this.status = REJECT;
                this.reason = reason;
                this.rejectCallBacks.forEach(fn=>fn())
            }
        }
        try{
            executor(resolve,reject)
        }catch(e){
            reject(e)
        }
       
    }
    then(onfulfilled,onrejected){
        let promise02 = new Promise((resolve,reject)=>{
            if(this.status === RESOLVE){
                try{
                    let x = onfulfilled(this.value);
                    resolve(x);
                }catch(e){
                    reject(e)
                }
               
            }
            if(this.status === REJECT){
                try{
                    let x = onrejected(this.reason);
                    resolve(x)
                }catch(e){
                    reject(e)
                }
                
            }
            if(this.status ===PENDING){
                this.resolveCallBacks.push(()=>{
                    try{
                        let x = onfulfilled(this.value);
                        resolve(x);
                    }catch(e){
                        reject(e)
                    }
                    
                });
                this.rejectCallBacks.push(()=>{
                    try{
                        let x = onrejected(this.reason);
                        resolve(x)
                    }catch(e){
                        reject(e)
                    }
                    
                });
            }
        })
        return promise02;
        
    }
}

module.exports = Promise;

【 手写源码 例 3 promise链式调用的原理:返回最新的Promise实例。promise实例可以链式调用,连续调用then,then()返回的是一个新的promise实例。下面的代码实现这个逻辑

const PENDING = 'PENDING';
const RESOLVE = 'RESOLVE';
const REJECT = 'REJECT';

function resolvePromise(promise02,x,resolve,reject){
    //如果拿到的x值 和新的promise02相等 就报出错误 (then的返回值跟then本身的调用的返回值一样的话 ) 
    if(x === promise02){
        return reject(new TypeError('[aa--Chaining cycle detected for promise #<Promise>]'))
    }
    let called;//开始没有值
    //对x返回值进行判断
    if(typeof x === 'object' && x!==null || typeof x === 'function'){
        try{
            let then = x.then;//取then的时候 可能发生异常
            if(typeof then ==='function'){
                //x是个promise实例
               then.call(x,y => {
                    //x是成功态的时候
                    if(called) return;
                    called = true;
                    resolvePromise(promise02,y,resolve,reject);                    
               },r=>{
                   //x是失败态
                   if(called) return;
                    called = true;
                   reject(r);
               })//x.then()
            }else{
                resolve(x);
            }
        }catch(err){
            if(called) return;
            called = true;
            reject(err);
        }        
    }else{
        //不是上述几种情况,那就是个 普通值
        resolve(x);
    }
}

function isPromise(value){
    if(typeof value === 'object' && value !==null || typeof value ==='function'){
        if(typeof value.then === 'function'){
            return true
        }
    }
    return false;
}

class Promise{
    constructor(executor){

        this.status = PENDING;

        this.value = undefined;
        this.reason = undefined;

        //当执行异步代码时:不知道结果是失败还是成功,先将成功和失败的逻辑存起来,当异步执行结束后再从这里去处置
        this.resolveCallBacks = [];
        this.rejectCallBacks = [];


        let resolve = (value)=>{
            
            if(value instanceof Promise){
                return value.then(resolve,reject)
            }//这个if是配合demo-5.js下面一段代码,当value的值是一个实例对象时,让这个value去调then

            if(this.status === PENDING){
                this.status = RESOLVE;
                this.value = value;
                this.resolveCallBacks.forEach(fn=>fn())
            }
        }
        let reject = (reason)=>{
            if(this.status === PENDING){
                this.status = REJECT;
                this.reason = reason;
                this.rejectCallBacks.forEach(fn=>fn())
            }
        }
        try{
            executor(resolve,reject)
        }catch(e){
            reject(e)
        }
       
    }
    then(onfulfilled,onrejected){
        onfulfilled = typeof onfulfilled === 'function' ? onfulfilled : val => val;
        onrejected = typeof onrejected === 'function' ? onrejected : err =>{throw err}
        let promise02 = new Promise((resolve,reject)=>{
            if(this.status === RESOLVE){
               setTimeout(()=>{ 
                    try{
                  
                         let x = onfulfilled(this.value);
                        //resolve(x); x 是 成功或者失败 的返回值
                        resolvePromise(promise02,x,resolve,reject)
                    }catch(e){
                        reject(e)
                    }
                })
            }
            if(this.status === REJECT){
                setTimeout(()=>{
                    try{
                    
                        let x = onrejected(this.reason);
                        //resolve(x)
                        resolvePromise(promise02,x,resolve,reject)
                    }catch(e){
                        reject(e)
                    }                    
                })                
            }
            if(this.status ===PENDING){
                this.resolveCallBacks.push(()=>{
                    setTimeout(()=>{
                        try{
                        
                            let x = onfulfilled(this.value);
                            //resolve(x);
                            resolvePromise(promise02,x,resolve,reject)
                        }catch(e){
                            reject(e)
                        }                        
                    })
                    
                });
                this.rejectCallBacks.push(()=>{
                    setTimeout(()=>{
                        try{
                        
                            let x = onrejected(this.reason);
                           // resolve(x)
                            resolvePromise(promise02,x,resolve,reject)
                        }catch(e){
                            reject(e)
                        }
                        
                    })
                    
                });
            }
        })
        return promise02;
        
    }
    catch(errCallback){
        return this.then(bull,errCallback);
    }

    static resolve(val){
        return new Promise((resolve,reject)=>{
            resolve(val);
        })
    }
    static reject(reason){
        return new Promise((resolve,reject)=>{
            reject(reason);
        })
    }
    //处理并发,前面传递的是个数组,对其进行循环 拿到 数组元素--每个实例
    static all(promises){
        return new Promise((resolve,reject)=>{
            let res = [];
            let index = 0;
            for (let i = 0; i<promises.length; i++){
                let promise = promises[i];
                if(isPromise(promise)){
                    promise.then((y)=>{
                        res[i] = y;
                        if(++index === promises.length){
                            resolve(res);
                        }
                    },()=>{
                        reject;
                    })
                }else{
                    res[i] = promise;
                    if(++index === promises.length){
                        resolve(res);
                    }
                }
                
            }
        })
    }

}

//根据Promise规范测试此文件是否符合规范 测试
Promise.defer = Promise.deferred = function(){
    let dfd = {};
    dfd.promise = new Promise((resolve,reject)=>{
        dfd.resolve = resolve;
        dfd.reject = reject;
    })
    return dfd;
}

//导出模块
module.exports = Promise;


 

 

 

 

好啦 这一篇先到这里。我的文章都是学习过程中的总结,如果发现错误,欢迎留言指出,我及时更正

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值