手动实现Promise(小白一枚,写的不对请多指教)

251 阅读4分钟

为什么要使用Promise?

大家在做前端开发时,对于异步任务的处理是比较头痛的,假如现在有这么一个异步任务的小例子:


function start(){
	setTimeout(function(){
            return "这里是我返回的结果";
        },3000)    	    
}
    
console.log(start());//undefined

结果我们打印出来undefined,至于为什么是undefined,我想大家都知道。所以我们通常的做法就是利用回调函数来获取到异步任务返回的值

function start(fn) {
        setTimeout(function () {
            fn("这里是我返回的结果")
        }, 3000)
    }

start(function (data) {
    console.log("这里对数据一次处理");
    a(data);
    function a(data) {
        console.log("这里对数据二次处理");
        b(data);
        function b(data) {
            console.log("这里对数据三次次处理");
        }
    }
})

这样我们代码的层次就一层一层嵌套,层数少了还好,但是层数一多,代码的可读性就太差了,不利于开发人员的后期维护。在这种场景下就出现Promise;

实现Promise的构造函数;

对于Promise的使用,我们这里就不讲了,大家可以看文档es6.ruanyifeng.com/#docs/promi… 我们实现我们自己的Promise构造函数。

function MyPromise(excutor){
    this.status="pending";//当前的状态,初始状态就是pending
    this.data=undefined;//回调的值
    this.callBack=[];//当前的回调函数
    
    function resolve(value){//成功的回调
    	//判断Promise当前是否是pending状态,因为promise只会从等待变为成功或者从等待变为失败
    	if(this.status!='pending'){
        	return;
        }
        this.status="resolve";
        this.data=value;
        //这里我们使用一个宏任务来实现我们的异步执行回调函数,确保我们可以拿到回调的值,再去执行回调函数
        setTimeout(()=>{
        	this.callBack.forEach(item=>{
            	console.log("这里执行我们的回调函数");//后面我们继续补全这里
            })
        })
    }
    
    function reject(reason){//失败的回调
    	if(this.status!='pending'){
        	return;
        }
        this.status="reject";
        this.data=value;
        setTimeout(()=>{
        	this.callBack.forEach(item=>{
            	console.log("这里执行我们的回调函数");//后面我们继续补全这里
            })
        })
    }
    
    try{
    	//执行构造函数,同时我们还需要监听构造函数在执行时,是否报错
        excutor(resolve.bind(this),reject.bind(this));
    }catch (err) {
    	//如果构造函数执行报错,那直接执行失败回调函数
    	reject.call(this,err);
    }	
}

实现.then方法

then方法可以传入两个回调函数,第一个是成功的回调函数,第二个是失败的回调函数

MyPromise.prototype.then=function(onResolve,onReject){
    let that=this;
    //如果没有传入成功回调函数,就默认返回回调的值,因为在Promise中有个值穿透的概念
    let onResolve=typeof onResolve=="function"?onResolve:value=>value;
    //如果没有传入失败的回调函数,就赋值一个抛出异常的函数
    let onReject=typeof onReject=="function"?onReject:err=>{
    	throw err;
    }
    //then方法返回一个新的Promise对象
    return new MyPromise((resolve,reject)=>{
    
    	function handler(on){
        	//获取回调函数返回的值
            let a=on(that.data);
            //判断返回值是否也是Promise对象,如果是就需要判断该Promise的状态来决定新返回的Promise的状态
            if(a instanceof MyPromise){
                a.then(data=>{
                    	resolve(data);
                 },err=>{
                    	reject(err);
                 })	
            }else{
                resolve(a);
            }
        }
    	//如果当前状态是pending,那么就需要将回调函数存入callBack中,当状态改变了就执行响应的回调
    	if(that.status=="pending"){
        	that.callBack.push({
            	onresolve:function(){
                	handler(onResolve);
                },
                onreject:function(){
                	handler(onReject);
                }
            })
        //如果状态已经是resolve,就执行成功回调函数   
        }else if(that.status=="resolve"){
            setTimeout(()=>{
            	handler(onResolve);
            })
         //如果状态已经是reject,就执行失败回调函数    
        }else{
           setTimeout(()=>{
            	handler(onReject)
            })
        }
    })
}

即现可以把我们的Promise构造函数补充完整:

function MyPromise(excutor){
    this.status="pending";//当前的状态,初始状态就是pending
    this.data=undefined;//回调的值
    this.callBack=[];//当前的回调函数
    
    function resolve(value){//成功的回调
    	//判断Promise当前是否是pending状态,因为promise只会从等待变为成功或者从等待变为失败
    	if(this.status!='pending'){
        	return;
        }
        this.status="resolve";
        this.data=value;
        //这里我们使用一个宏任务来实现我们的异步执行回调函数,确保我们可以拿到回调的值,再去执行回调函数
        setTimeout(()=>{
        	this.callBack.forEach(item=>{
            	item.onresolve(value);//++++++++++++++++++++++++
            })
        })
    }
    
    function reject(reason){//失败的回调
    	if(this.status!='pending'){
        	return;
        }
        this.status="reject";
        this.data=value;
        setTimeout(()=>{
        	this.callBack.forEach(item=>{
            	item.onreject(reason);//++++++++++++++++++++++++++
            })
        })
    }
    
    try{
    	//执行构造函数,同时我们还需要监听构造函数在执行时,是否报错
        excutor(resolve.bind(this),reject.bind(this));
    }catch (err) {
    	//如果构造函数执行报错,那直接执行失败回调函数
    	reject.call(this,err);
    }	
}

实现catch方法

有了then方法,catch方法就比较简单了,直接执行失败的回调函数即可

MyPromise.prototype.catch = function (onReject) {
     return this.then(undefined, onReject);
}

实现resolve静态方法

MyPromise.resolve = function (value) {
            return new MyPromise((resolve, reject) => {
                //promise.resolve,传入值是promise类型,就需要判断传入promise的状态来决定返回promise的状态
                if (value instanceof MyPromise) {
                    value.then(data => {
                        resolve(data);
                    }).catch(err => {
                        reject(err);
                    })
                } else {
                    //promise.resolve,传入值是普通类型,就直接返回成功,
                    resolve(value);
                }
            })
}

实现reject静态方法

MyPromise.reject = function (value) {
            //promise.reject是只能返回一个失败的promise
            return new MyPromise((resolve, reject) => {
                reject(value);
            })
}

实现all静态方法

MyPromise.all = function (arr = []) {
            //promise.all:只要传入的promise数组中有失败的,就直接返回失败的promise,全部成功则返回成功promise,值为成功值数组
            return new MyPromise((resolve, reject) => {
                var list = [];
                var n = 0;
                arr.forEach((item, index) => {
                    MyPromise.resolve(item).then(data => {
                        n++;
                        list.push(data);
                        if (n === arr.length) {
                            resolve(list)
                        }
                    }).catch(err => {
                        reject(err);
                    })
                })
            })
}

实现race静态方法

MyPromise.race = function (arr = []) {
            return new MyPromise((resolve, reject) => {
                arr.forEach(item => {
                    item.then(data => {
                        resolve(data)
                    }).catch(err => {
                        reject(err)
                    })
                })
            })
}

谢谢大家!...........