IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> JavaScript知识库 -> 手写一个Promise -> 正文阅读

[JavaScript知识库]手写一个Promise

一、Promise的执行原理:

Promise对象首先存在三种状态:pendding、fulfilled、rejected。在通过new Promise的时候,Promise内部默认的状态是pendding,Promise的状态一旦改变后就不会再进行改变。例如用原生的Promise举个例子:

{
    let p = new Promise((resolve, reject) => {
        resolve('OK');
        reject('error');
    }).then(res => {
        console.log("resolve结果", res);
    }).catch(err => {
        console.log("reject结果", err);
    })
    //输出的结果: resolve结果 OK
    //再resolve('OK')之后,那么实例p的状态就改变了,所以后面的reject('error')不能再改变实例p的状态,最后catch就不会执行。
}

Promise的状态改变只有两种类型:从pendding==>fulfilled或者pendding==>rejected。状态的修改途径:1.通过执行resolve函数把状态修改为fulfilled状态。2.通过reject函数把状态修改为rejected状态。3.在excutor函数中try到错误会把状态改为rejected。对第三种情况举个例子:

{
    let p = new Promise((resolve, reject) => {
       throw new Error("出错了")
    })
    console.log(p); //p实例的状态是rejected
}

Promise的基本使用:

1.创建实例

let p = new Promise((resolve, reject) => {
        //具体操作
    })
    //通过new创建实例p,Promise接收一个excutor函数作为参数,且该函数会立即执行。
    // let p = new Promise(excutor),excutor接收两个resolve,reject函数。

2. .then回调
.then函数,可以接受两个回调函数参数,第一个回调处理resolve成功后的下一步操作,第二个处理rejected失败的下一步操作,同时.then函数会返回一个新的Promise对象,这里保证了可以进行链式调用,.then后可再次进行.then。

let p = new Promise((resolve, reject) => {
        resolve("OK");
    });
    p.then(res => {
        console.log(res);
        return '第二次OK'
    }).then(res => {
        console.log(res);
    })
    //输出结果:OK
    //		   第二次OK
    

3. .catch
.catch方法进行处理通过reject()执行后的下一步操作,对错误进行处理。

let p = new Promise((resolve, reject) => {
        reject("Error")
    });
    p.catch(err => {
        console.log(err);
    })
    //输出:Error
    //在catch之后是可以再进行.then操作的。

了解了Promise的基本使用,现在一步一步的去实现MyPromise

二、手写Promsie

借鉴的文章:https://mengera88.github.io/2017/05/18/Promise原理解析/
第一步初始化:首先是通过new创建的,且接收一个excutor函数,接收2个参数:resolve、reject,resolve是Promise内部的函数用于把状态改为fulfilled,reject是Promise内部的函数用于把状态改为rejected,并且Promise的初始状态是pendding,所以列出如下结构:

function MyPromise(executor) {
    this.promiseStatus = 'pendding'; //初始化实例的状态为 pendding
    this.promiseValue = null; //保存resolve、reject函数传递的参数。
    function resolve(data) {
         //把状态改为fulfilled
    }
    function reject(data) {
         //把状态改为rejected
    }
	executor(resolve, reject); //该函数是立即执行,则一进来直接调用。
}

测试一下:

let p = new MyPromise((resolve, reject) => {});
console.log(p);

输出结果:
在这里插入图片描述
第二步修改MyPromise的状态:在excutor中调用resolve或者reject函数进行改变MyPromise的状态,修改promiseStatus的值。
结构如下:

function MyPromise(executor) {
        this.promiseStatus = 'pendding'; //初始化实例的状态为 pendding
        this.promiseValue = null; //保存resolve、reject函数传递的参数。
        const self = this; //这里先进行保存上下文。
        
        function resolve(data) {
            //把状态改为fulfilled
            self.promiseStatus = 'fulfilled'; //这里的this指向的是window,所以使用self访问MyPromise
        }
        function reject(error) {
            //把状态改为rejected
            self.promiseStatus = 'rejected';
        }
        executor(resolve, reject); //该函数是立即执行,则一进来直接调用。
    }

测试一下:

let p = new MyPromise((resolve, reject) => {
            resolve('OK');
            reject('error');
        });
        console.log(p);

输出结果:
在这里插入图片描述
输出结果没错,也没报错,但是不符合Promise的逻辑,因为状态一旦改变就不再会改变状态。意思是resolve()或者reject()只能执行其中一个,这里就错了,先执行了resolve('OK'),然后执行了reject(''error),所以该状态变为了rejected。
修改后的代码:


```javascript
function MyPromise(executor) {
        this.promiseStatus = 'pendding'; //初始化实例的状态为 pendding
        this.promiseValue = null; //保存resolve、reject函数传递的参数。
        const self = this; //这里先进行保存上下文。
		executor(resolve, reject); //该函数是立即执行,则一进来直接调用。
		
        function resolve(data) {
            //把状态改为fulfilled
            //这里的this指向的是window,所以使用self访问MyPromise
            if (self.promiseStatus === 'pendding') {
                self.promiseStatus = 'fulfilled'; 
            }
        }
        function reject(data) {
            //把状态改为rejected
            if (self.promiseStatus === 'pendding') {
                self.promiseStatus = 'rejected';
            }
        }
    }

第三步处理resolve()、reject()的参数
把参数进行保存到Promise的promiseValue中,便于在.then和.catch的回调函数中访问。
修改后的代码:

function MyPromise(executor) {
        this.promiseStatus = 'pendding'; //初始化实例的状态为 pendding
        this.promiseValue = null; //保存resolve、reject函数传递的参数。
        const self = this; //这里先进行保存上下文。

        function resolve(data) {
            //把状态改为fulfilled
            //这里的this指向的是window,所以使用self访问MyPromise
            if (self.promiseStatus === 'pendding') {
                self.promiseStatus = 'fulfilled'; 
                self.promiseValue = data;
            }
        }
        function reject(data) {
            //把状态改为rejected
            if (self.promiseStatus === 'pendding') {
                self.promiseStatus = 'rejected';
                self.promiseValue = data;
            }
        }
        executor(resolve, reject); //该函数是立即执行,则一进来直接调用。
    }

测试一下:

let p = new MyPromise((resolve, reject) => {
            resolve('OK');
            reject('error');
        });
        console.log(p);

测试结果:
在这里插入图片描述

执行了resolve('OK')之后就没有执行reject(‘error’),保证了修改了状态之后不能再改,同时resolve('OK')中的参数保存到了promiseValue。

第四步完成.then函数链式调用,.then()接收2个参数,第一个回调是处理resolve函数,第二个处理reject函数,而且.then会返回一个新的Promise,新的Promise的Value是回调返回的值,一个老的Promise和新的Promise怎么连接上呢。

这里最重要的部分来了:用一个bridge函数,该函数接收一个对象参数,该对象保存了老的Promise的回调onResolve和新的Promise的resolve函数,通过老的Promise的onResolve执行后会得到一个返回值,该返回值是给下一个新Promise的,然后再调用新Promise的resolve方法就可以把值进行传递了。

function MyPromise(executor) {
        this.promiseStatus = 'pendding'; //初始化实例的状态为 pendding
        this.promiseValue = null; //保存resolve、reject函数传递的参数。
        const self = this; //这里先进行保存上下文。
        executor(resolve, reject); //该函数是立即执行,则一进来直接调用。
        function resolve(data) {
            //把状态改为fulfilled
            //这里的this指向的是window,所以使用self访问MyPromise
            if (self.promiseStatus === 'pendding') {
                self.promiseStatus = 'fulfilled'; 
                self.promiseValue = data;  //在resolve(data),执行后把data放入self.promiseValue,然后在.then()中的回调能够访问参数。
            }
        }
        function reject(data) {
            //把状态改为rejected
            if (self.promiseStatus === 'pendding') {
                self.promiseStatus = 'rejected';
                self.promiseValue = data;
            }
        }
        function bridge (callBack) {
            if (!callBack.onResolve) { // 判断then中传的回调是否存在,不存在回调,就直接执行新的promise的resolve
                callBack.resolve(self.promiseValue);
                return;
            }
                // .then存在回调函数就执行回调函数
            let data = callBack.onResolve(self.promiseValue);
            callBack.resolve(data); //回调执行完后在执行新的Promise的resolve()返回同时把结果传给新的Promise
    	}
    	
        MyPromise.prototype.then = function(onResolve) {
            return new MyPromise(resolve => {
            	bridge({onResolve: onResolve || null, resolve });
            })
        }
    }

测试一下:

let p = new MyPromise((resolve) => {
            resolve('OK');
        }).then(res => {
            console.log(res);
        })
        //输出 OK
        //在进行执行resolve('OK')的时候,把'OK'保存到MyPromise的promiseValue中,然后在调用.then中的回调的时候,把该参数传给该回调函数,这里的res就是访问的MyPromise中的promiseValue的值。

第五步:错误处理(reject)。
1.当在excutor函数中抛出了错误,得直接修改MyPromise的状态为rejected。
2.或者通过reject(err)执行把状态改为rejected。
进入.catch()函数的前提:reject()执行后,且.then()函数中没有第二个参数处理失败结果。
加上.catch方法后的代码:

function MyPromise(executor) {
        this.promiseStatus = 'pendding'; //初始化实例的状态为 pendding
        this.promiseValue = null; //保存resolve、reject函数传递的参数。
        const self = this; //这里先进行保存上下文。
        try{  //用try去捕获excutor报的错误
            executor(resolve, reject); //该函数是立即执行,则一进来直接调用。
        } catch(err) {
            reject(err);
        }
        
        function resolve (data) {
            //把状态改为fulfilled
            //这里的this指向的是window,所以使用self访问MyPromise
            if (self.promiseStatus === 'pendding') {
                self.promiseStatus = 'fulfilled'; 
                self.promiseValue = data;  //在resolve(data),执行后把data放入self.promiseValue,然后在.then()中的回调能够访问参数。
            }
        }
        function reject (data) {
            //把状态改为rejected
            if (self.promiseStatus === 'pendding') {
                self.promiseStatus = 'rejected';
                self.promiseValue = data;
            }
        }
        function bridge(callBack) {
        	if (self.promiseStatus === 'fulfilled') {  //成功状态
            	if (!callBack.onResolve) { // 判断then中传的回调是否存在,不存在回调,就直接执行新的promise的resolve
                	callBack.resolve(self.promiseValue);
                	return
            	}
                // .then存在回调函数就执行回调函数
            	let data = callBack.onResolve(self.promiseValue);
            	callBack.resolve(data); //回调执行完后在执行新的Promise的resolve()返回同时把结果传给新的Promise
        	}

        	if (self.promiseStatus === 'rejected') { //失败状态
            	if (!callBack.onReject) {// 判断then中传的回调是否存在,不存在回调,就直接执行新的promise的reject
                	callBack.reject(self.promiseValue);
                	return;
            	}
            	let error = callBack.onReject(self.promiseValue);
            	callBack.resolve(error);
        	}
    	}
        this.then = function (onResolve, onReject) {
         //.then进来就返回一个新的Promsie,然后会进入到handle函数
            return new MyPromise((resolve, reject) => {
            	bridge({
                	onResolve: onResolve || null, //避免then中不存在参数时代码报错
                	onReject: onReject || null,
                	reject,
                	resolve
            	})
        	})
        }
        this.catch = function (onReject) {
        	return new MyPromise(resolve => { //通过catch函数处理后返回的Promise是一个fulfilled状态,所以新的Promise提供resolve不提供rejected
            	bridge({
                	onReject: onReject || null,
                	resolve
            	})
        	})
    	}
    }

测试一下:

//用catch处理
let p = new MyPromise((resolve, reject) => {
            reject("error")
        }).catch(err => {
            console.log(err);
        })
//用.then第二个回调参数处理失败
let p = new MyPromise((resolve, reject) => {
            reject("error")
        }).then(() => {}, err => {
            console.log(err);
        })
       //两次的输出结果都是:error。

上面的输出好像看似没问题,.then .catch的测试都没有什么问题,以上的测试都是在测试同步代码,然而Promise的用途就是处理异步操作。
第六步:这时候来试一下在excutor中加入定时器(setTimeout)来模拟异步操作。
加了定时器测试代码如下:

测试代码:
let p = new MyPromise((resolve, reject) => {
            setTimeout(() => {
                resolve("OK");
            }, 1000)
        }).then( res => {
            console.log(res);
        })
     //什么也没有输出。

这时候会想:1s后执行了resolve(‘OK’),为什么没输出’OK‘呢。



再改一下测试代码,把.then的执行延迟2000ms执行看看:

let p = new MyPromise((resolve, reject) => {
            setTimeout(() => {
                resolve("OK");
            }, 1000)
        })
        setTimeout(() => {
            p.then( res => {
                console.log(res);
            })
        }, 2000)
 //很神奇,这时候就输出了:OK

原因:当正常书写了.then后,那么这时候会立即执行.then函数,然而这时候的resolve(’OK‘)还没开始执行,得1s后再执行,所以这时候的状态还是pendding,再看看.then函数和bridge函数的逻辑,不满足条件就不会输出什么,只有在resolve(‘OK’)执行后再调用bridge就会正确的执行。

怎么解决呢?

解决办法:通过回调函数处理。进入bridge函数后判断状态,pendding状态就先把回调函数保存起来,在resolve()函数执行的时候再去循环调用这些回调函数。
修改后的代码:

function MyPromise(executor) {
    // executor是在new Promise((resolve, reject) => {})中传的函数
    //在一开始进行new的时候,promise的状态就是pendding。
    this.promiseStatus = 'pendding';
    this.promiseValue = null;
    let callBackList = []
    const self = this;
    try {
        executor(resolve, reject);
    } catch(e) {
        reject(e);
    }
    function resolve (data) {
        if (self.promiseStatus === 'pendding') {
            self.promiseValue = data;
            self.promiseStatus = 'fulfilled';
            callBackList.forEach(objFun => {
                bridge(objFun);
            })
        }
    }
    function reject (error) {
        if (self.promiseStatus === 'pendding') {
            self.promiseValue = error;
            self.promiseStatus = 'rejected';
            callBackList.forEach(objFun => {
                bridge(objFun);
            })
        }
    }
    function bridge (callBack) {
        //一进来判断状态,pendding就把对象{onResolve: onResolve, resolve: resolve}保存到callBackList中。
        if (self.promiseStatus === 'pendding') {   //只有在excutor中存在异步代码才会把回调进行临时保存。
            callBackList.push(callBack);
            return;
        }
        //如果excutor中的函数没有异步的话,就直接执行下面的代码   或者resolve执行后进入下面的代码
        if (self.promiseStatus === 'fulfilled') {  //成功状态
            if (!callBack.onResolve) { // 判断then中传的回调是否存在,不存在回调,就直接执行新的promise的resolve
                callBack.resolve(self.promiseValue);
                return
            }
                // .then存在回调函数就执行回调函数
            let data = callBack.onResolve(self.promiseValue);
            callBack.resolve(data); //回调执行完后在执行新的Promise的resolve()返回同时把结果传给新的Promise
        }

        if (self.promiseStatus === 'rejected') { //失败状态
            if (!callBack.onReject) {
                callBack.reject(self.promiseValue);
                return;
            }
            let error = callBack.onReject(self.promiseValue);
            callBack.resolve(error);
        }
    }
    this.then = function (onResolve, onReject) {
        //.then进来就返回一个新的Promsie,然后会进入到handle函数
        return new MyPromise((resolve, reject) => {
            bridge({
                onResolve: onResolve || null, //避免then中不存在参数时代码报错
                onReject: onReject || null,
                reject,
                resolve
            })
        })
    }
    this.catch = function (onReject) {
        return new MyPromise(resolve => {
            bridge({
                onReject: onReject || null,
                resolve
            })
        })
    }
}

测试代码:

let p = new MyPromise((resolve, reject) => {
            setTimeout(() => {
                resolve("OK");
            }, 1000)
        }).then(res => {
            console.log(res);
        })
        //在1s后输出OK。

let p = new MyPromise((resolve, reject) => {
            setTimeout(() => {
                reject("error");
            }, 1000)
        }).then(() => {}, err => {
            console.log(err);
        })
        // 1s后输出error


       let p = new MyPromise((resolve, reject) => {
            setTimeout(() => {
                resolve('OK')
            }, 1000)
        }).then(res => {
            console.log(res);
            return '55555555'
        }).then(res => {
            console.log(res);
        })
    	//1s后输出: OK  55555555
    	//链式调用和异步处理没问题

封装的全部代码:

function MyPromise(executor) {
    // executor是在new Promise((resolve, reject) => {})中传的函数
    //在一开始进行new的时候,promise的状态就是pendding。
    this.promiseStatus = 'pendding';
    this.promiseValue = null;
    let callBackList = []
    const self = this;
    try {
        executor(resolve, reject);
    } catch(e) {
        reject(e);
    }
    function resolve (data) {
        if (self.promiseStatus === 'pendding') {
            self.promiseValue = data;
            self.promiseStatus = 'fulfilled';
            callBackList.forEach(objFun => {
                bridge(objFun);
            })
        }
    }
    function reject (error) {
        if (self.promiseStatus === 'pendding') {
            self.promiseValue = error;
            self.promiseStatus = 'rejected';
            callBackList.forEach(objFun => {
                bridge(objFun);
            })
        }
    }
    function bridge (callBack) {
        //一进来判断状态,pendding就把对象{onResolve: onResolve, resolve: resolve}保存到callBackList中。
        if (self.promiseStatus === 'pendding') {   //只有在excutor中存在异步代码才会把回调进行临时保存。
            callBackList.push(callBack);
            return;
        }
        //如果excutor中的函数没有异步的话,就直接执行下面的代码   或者resolve执行后进入下面的代码
        if (self.promiseStatus === 'fulfilled') {  //成功状态
            if (!callBack.onResolve) { // 判断then中传的回调是否存在,不存在回调,就直接执行新的promise的resolve
                callBack.resolve(self.promiseValue);
                return
            }
                // .then存在回调函数就执行回调函数
            let data = callBack.onResolve(self.promiseValue);
            callBack.resolve(data); //回调执行完后在执行新的Promise的resolve()返回同时把结果传给新的Promise
        }

        if (self.promiseStatus === 'rejected') { //失败状态
            if (!callBack.onReject) {
                callBack.reject(self.promiseValue);
                return;
            }
            let error = callBack.onReject(self.promiseValue);
            callBack.resolve(error);
        }
    }
    this.then = function (onResolve, onReject) {
        //.then进来就返回一个新的Promsie,然后会进入到handle函数
        return new MyPromise((resolve, reject) => {
            bridge({
                onResolve: onResolve || null, //避免then中不存在参数时代码报错
                onReject: onReject || null,
                reject,
                resolve
            })
        })
    }
    this.catch = function (onReject) {
        return new MyPromise(resolve => {
            bridge({
                onReject: onReject || null,
                resolve
            })
        })
    }
}
  JavaScript知识库 最新文章
ES6的相关知识点
react 函数式组件 & react其他一些总结
Vue基础超详细
前端JS也可以连点成线(Vue中运用 AntVG6)
Vue事件处理的基本使用
Vue后台项目的记录 (一)
前后端分离vue跨域,devServer配置proxy代理
TypeScript
初识vuex
vue项目安装包指令收集
上一篇文章      下一篇文章      查看所有文章
加:2022-03-04 15:27:11  更:2022-03-04 15:28:42 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 8:27:46-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码