最新要闻

广告

手机

iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?

iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?

警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案

警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案

家电

手写Promise教程_then方法|今日热门

来源:博客园

手写Promise教程_then方法

1、Promise结构分析

console.log(new Promise((resolve, reject) => {}));

通过打印Promise的实例对象,观察promise的对象结构:

分析上图,可以得出一些信息:

  • 1、promise实例对象上有PromiseState属性和PromiseResult属性
  • 2、Promise构造函数上有all方法、race方法、resolve方法、reject方法。
  • 3、Promise构造函数的原型对象上有then方法、catch方法。

这些是Promise最核心的属性和方法,手写Promise也就是去实现它们的功能。


(资料图片)

2、构造函数

创建一个promise_test.js文件,被在html文件中中引入:

index.html:

                手写promise教程    <script src="./promise_test.js"></script>    <script>        new Promise((resolve, reject) => {});    </script>

promise_test.js:

function Promise(executor) {    // 添加属性    this.PromiseState = "pending";    this.PromiseResult = undefined;    executor(resolve, reject);}

在promise_test.js文件中写上一个Promise构造函数,把JS为我们提供的原生Promise构造函数覆盖掉,然后运行代码,报错:

Uncaught ReferenceError: resolve is not defined。

为什么会出现这个错误呢?

因为在index.html中new Promise( 匿名函数(resolve, reject) ),作为实参传进来的匿名函数(resolve, reject),在构造函数内调用的时候,传入了resolve和reject实参方法,但构造函数中没有这两个方法,因此我们要在构造函数中定义resolve和reject这两个方法:

2.1、resolve 和 reject

功能分析:

  • 1、修改PromiseState的状态为fulfilled,并且PromiseState的状态只能修改一次。
  • 2、可以接收一个参数,将这个参数的值赋给PromiseResult。

index.html:

new Promise((resolve, reject) => { resolve("test"); });

promise_test.js:

function Promise(executor) {    // 添加属性    this.PromiseState = "pending";    this.PromiseResult = undefined;    // 定义resolve函数    function resolve(data) {      console.log(this.PromiseState); // undefined    }    executor(resolve, reject);}

既然要修改PromiseState的值,那么我们先尝试获取这个值,构造函数中声明的是pending,但结果却是:undefined,为什么?

this的指向问题,我们在代码中打印了了一下this,结果:Window,为什么是Window?

这里复习一下this的指向,1、普通函数中的this指向window。2、构造函数中的this指向实例对象。3、箭头函数中的this指向父级作用域的this。

ok,如果知道了这两点的话,那么就可以知道如何解决:1、将this的指向作为一个属性保存下来。2、使用箭头函数。

这里选择保存this的指向:

function Promise(executor) {    // 添加属性    this.PromiseState = "pending";    this.PromiseResult = undefined;    // 保存实例对象的this值    const _this = this;    // 定义resolve函数    function resolve(data) {        // 判断状态,promise状态只能更改一次        if (_this.PromiseState !== "pending") return;        // 1、修改对象的状态(promiseState)        _this.PromiseState = "fulfilled"; // resolved        // 2、设置对象的结果值(promiseResult)        _this.PromiseResult = data;    }    function reject(data) {        // 判断状态,promise状态只能更改一次        if (_this.PromiseState !== "pending") return;        // 1、修改对象的状态(promiseState)        _this.PromiseState = "rejected"; // rejected        // 2、设置对象的结果值(promiseResult)        _this.PromiseResult = data;    }    executor(resolve, reject);}

index.html:

测试1:

const p = new Promise((resolve, reject) => {     resolve("success");});console.log(p);

结果1:Promise{PromiseState: "fulfilled", PromiseResult: "success"}

——

测试2:

const p = new Promise((resolve, reject) => {     reject("fail");});console.log(p);

结果2:Promise{PromiseState: "rejected", PromiseResult: "fail"}

——

经过测试,PromiseState和PromiseResult成功被修改!

3、then方法

3.1、功能分析

通过1的分析,我们可以得知then方法是放在原型对象上的,这样做的目的是为了通过实例对象调用.then进行链式调用

链式调用是Promise最重要的特性,它解决了回调地狱的问题,下面我们来分析一下then方法的功能:

1、Promise.prototype.then方法:then(onResolved, onRejected) => {},

它传入两个回调函数作为形参,onResolved作为PromiseState为fulfilled时的回调函数,onRejected作为PromiseState为rejected时的回调函数。

验证代码:

const p = new Promise((resolve, reject) => {    // resolve将PromiseState状态改为了fulfilled    resolve("success");    // reject将PromiseState状态改为了rejected    // reject("fail");});p.then(value => {    // onResolved方法的回调函数,状态时fulfilled执行    console.log(value); // success}, reason => {    // onRejected方法的回调函数,状态时rejected执行    console.log(reason); // fail});

2、then方法的返回值是一个新的promise对象,这个新的promise对象的结果状态由then指定的回调函数执行的返回值决定。

  • 如果then回调函数的返回值是:非Promise的数据。那么新promise的状态(PromiseState)是fulfilled,值(PromiseResult)是这个非Promise的数据。
  • 如果then回调函数的返回值是:Promise类型的数据,那么新的promise的状态(PromiseState)由返回值promise对象的状态决定,值(PromiseResult)也由它的值决定。
  • 如果抛出异常,新promise变成rejected,reason为抛出的异常。

验证代码:

const p = new Promise((resolve, reject) => {    // resolve将PromiseState状态改为了fulfilled    resolve("success");    // reject将PromiseState状态改为了rejected    // reject("fail");});const p2 = p.then(value => {    // onResolved方法的回调函数,状态时fulfilled执行下面的代码    // 返回值是value变量,非promise,也就是"success"    // return value;    // 返回值是promise对象    return new Promise((resolve, reject) => {        // 如果调用resolve,那么p2的PromiseStatus就是fulfilled,PromiseResult是success2        resolve("success2");        // 如果调用reject,那么p2的PromiseStatus就是rejected,PromiseResult是fail2        // reject("fail2");        // 如果抛出异常,那么p2的PromiseStatus就是rejected,PromiseResult是error        // throw "error";    });}, reason => {    // onRejected方法的回调函数,状态时rejected执行下面的代码    console.log(reason); // fail});console.log(p2);

3、链式调用,并且异步调用。

  • 1、then里面的代码,会按照链式调用的顺序,顺序执行。
  • 2、then里面的代码,是异步执行。

验证代码:

const p = new Promise((resolve, reject) => {    resolve();    console.log("111");});const p2 = p.then(value => {    // 没有return返回值,默认是return undefined,这个也是非promise的数据,因此状态是fulfilled,值是undefined    console.log("222");}, reason => {    console.log(reason); // fail});p2.then(value => {    console.log("333");});console.log("444");

执行结果:111 444 222 333

可以看到,同步代码先执行,执行完之后再执行then中的异步代码,并且是按照链式操作的顺序执行下来。

以上就是then方法的功能分析,下面是手写实现:

3.2、功能实现

1、实现异步执行 + 判断返回值进行不同的操作

Promise.prototype.then = function(onResolved, onRejected) {    // then函数的返回值是一个新的promise对象    return new Promise((resolve, reject) => {        // 如果实例对象上的PromiseState是fulfilled,执行onResolved        if (this.PromiseState === "fulfilled") {            // 异步执行            setTimeout(() => {                try {                    let result = onResolved(this.PromiseResult);                    // 如果返回值是promise对象                    if (result instanceof Promise) {                        result.then(v => {                            resolve(v);                        }, r => {                            reject(r);                        });                    } else {                        // 如果返回值是非promise对象                        resolve(result);                    }                } catch (e) {                    // 如果抛出异常,新promise变成rejected,reason为抛出的异常。                    reject(e);                }            });        }        // 如果实例对象上的PromiseState是fulfilled,执行onRejected        if (this.PromiseState === "rejected") {            // 异步执行            setTimeout(() => {                try {                    let result = onRejected(this.PromiseResult);                    // 如果返回值是promise对象                    if (result instanceof Promise) {                        result.then(v => {                            resolve(v);                        }, r => {                            reject(r);                        });                    } else {                        // 如果返回值是非promise对象                        resolve(result);                    }                } catch (e) {                    // 如果抛出异常,新promise变成rejected,reason为抛出的异常。                    reject(e);                }            });        }    });}

上述代码看似可以完美实现,但是却存在一个缺陷,以下是测试index.html:

const p = new Promise((resolve, reject) => {    // 如果这里是异步的,出现bug:永远不会触发then方法    setTimeout(() => {        resolve("111");    }, 1000);});const p2 = p.then(value => {    console.log("then中:" + value);}, reason => {    console.log(reason);});

上述测试,及时是等一秒甚至一百秒,then方法中的回调也不会执行,为什么?

因为这个时候p这个对象中的promiseState还处于pending状态,所以我们要在then方法中加上第三种情况,pending。

做法是:将回调函数保存起来,在resolve和reject方法中去调用,如此就可以实现链式调用

2、链式调用的实现

promise_test.js:

function Promise(executor) {    // 添加属性    this.PromiseState = "pending";    this.PromiseResult = undefined;    // 用于保存回调函数的数组    this.callbacks = [];    // 保存实例对象的this值    const _this = this;    // 定义resolve函数    function resolve(data) {        // 判断状态,promise状态只能更改一次        if (_this.PromiseState !== "pending") return;        // 1、修改对象的状态(promiseState)        _this.PromiseState = "fulfilled"; // fulfilled        // 2、设置对象的结果值(promiseResult)        _this.PromiseResult = data;        // 3、调用异步任务下,成功的回调函数        setTimeout(() => {            _this.callbacks.forEach(item => {                item.Resolved(data);            });        });    }    function reject(data) {        // 判断状态,promise状态只能更改一次        if (_this.PromiseState !== "pending") return;        // 1、修改对象的状态(promiseState)        _this.PromiseState = "rejected"; // rejected        // 2、设置对象的结果值(promiseResult)        _this.PromiseResult = data;        // 3、调用异步任务下,失败的回调函数        setTimeout(() => {            _this.callbacks.forEach(item => {                item.Rejected(data);            });        });    }    executor(resolve, reject);}Promise.prototype.then = function(onResolved, onRejected) {    // then函数的返回值是一个新的promise对象    return new Promise((resolve, reject) => {        // 如果实例对象上的PromiseState是fulfilled,执行onResolved        if (this.PromiseState === "fulfilled") {            // 异步执行            setTimeout(() => {                try {                    let result = onResolved(this.PromiseResult);                    // 如果返回值是promise对象                    if (result instanceof Promise) {                        result.then(v => {                            resolve(v);                        }, r => {                            reject(r);                        });                    } else {                        // 如果返回值是非promise对象                        resolve(result);                    }                } catch (e) {                    // 如果抛出异常,新promise变成rejected,reason为抛出的异常。                    reject(e);                }            });        }        // 如果实例对象上的PromiseState是rejected,执行onRejected        if (this.PromiseState === "rejected") {            // 异步执行            setTimeout(() => {                try {                    let result = onRejected(this.PromiseResult);                    // 如果返回值是promise对象                    if (result instanceof Promise) {                        result.then(v => {                            resolve(v);                        }, r => {                            reject(r);                        });                    } else {                        // 如果返回值是非promise对象                        resolve(result);                    }                } catch (e) {                    // 如果抛出异常,新promise变成rejected,reason为抛出的异常。                    reject(e);                }            });        }        // 如果new Promise中的函数是异步的,例如一秒之后再调用resolveo或者reject的情况        // 后续的新promise对象的状态都是pending,都会走这里。        // 我们就将回调函数保存起来,在resolve和reject方法中去调用        if (this.PromiseState === "pending") {            // 注意保存一下this的指向,因为下面两个都是普通函数,内部的this指向是window            const _this = this;            function Resolved() {                try {                    let result = onResolved(_this.PromiseResult);                    // 如果返回值是promise对象                    if (result instanceof Promise) {                        result.then(v => {                            resolve(v);                        }, r => {                            reject(r);                        });                    } else {                        // 如果返回值是非promise对象                        resolve(result);                    }                } catch (e) {                    // 如果抛出异常,新promise变成rejected,reason为抛出的异常。                    reject(e);                }            }            function Rejected() {                try {                    let result = onRejected(_this.PromiseResult);                    // 如果返回值是promise对象                    if (result instanceof Promise) {                        result.then(v => {                            resolve(v);                        }, r => {                            reject(r);                        });                    } else {                        // 如果返回值是非promise对象                        resolve(result);                    }                } catch (e) {                    // 如果抛出异常,新promise变成rejected,reason为抛出的异常。                    reject(e);                }            }            this.callbacks.push({                Resolved,                Rejected            });        }    });}

3、链式调用的实现_代码优化

可以看出,上述有很多重复代码,是为了更好帮助理解,故意的。

以下则是优化后的代码promise_test.js:

function Promise(executor) {    // 添加属性    this.PromiseState = "pending";    this.PromiseResult = undefined;    // 用于保存回调函数的数组    this.callbacks = [];    // 保存实例对象的this值    const _this = this;    // 定义resolve函数    function resolve(data) {        // 判断状态,promise状态只能更改一次        if (_this.PromiseState !== "pending") return;        // 1、修改对象的状态(promiseState)        _this.PromiseState = "fulfilled"; // fulfilled        // 2、设置对象的结果值(promiseResult)        _this.PromiseResult = data;        // 3、调用异步任务下,成功的回调函数        setTimeout(() => {            _this.callbacks.forEach(item => {                item.Resolved(data);            });        });    }    function reject(data) {        // 判断状态,promise状态只能更改一次        if (_this.PromiseState !== "pending") return;        // 1、修改对象的状态(promiseState)        _this.PromiseState = "rejected"; // rejected        // 2、设置对象的结果值(promiseResult)        _this.PromiseResult = data;        // 3、调用异步任务下,失败的回调函数        setTimeout(() => {            _this.callbacks.forEach(item => {                item.Rejected(data);            });        });    }    executor(resolve, reject);}Promise.prototype.then = function(onResolved, onRejected) {    // 注意保存一下this的指向,因为下面普通函数,内部的this指向是window    const _this = this;    // then函数的返回值是一个新的promise对象    return new Promise((resolve, reject) => {        function handler(func) {            try {                let result = func(_this.PromiseResult);                // 如果返回值是promise对象                if (result instanceof Promise) {                    result.then(v => {                        resolve(v);                    }, r => {                        reject(r);                    });                } else {                    // 如果返回值是非promise对象                    resolve(result);                }            } catch (e) {                // 如果抛出异常,新promise变成rejected,reason为抛出的异常。                reject(e);            }        }        // 如果实例对象上的PromiseState是fulfilled,执行onResolved        if (this.PromiseState === "fulfilled") {            // 异步执行            setTimeout(() => {                handler(onResolved);            });        }        // 如果实例对象上的PromiseState是rejected,执行onRejected        if (this.PromiseState === "rejected") {            // 异步执行            setTimeout(() => {                handler(onRejected);            });        }        // 如果new Promise中的函数是异步的,例如一秒之后再调用resolveo或者reject的情况        // 后续的新promise对象的状态都是pending,都会走这里。        // 我们就将回调函数保存起来,在resolve和reject方法中去调用        if (this.PromiseState === "pending") {            this.callbacks.push({                Resolved: () => {                    handler(onResolved);                },                Rejected: () => {                    handler(onRejected);                }            });        }    });}

测试代码index.html:

const p = new Promise((resolve, reject) => {    // 开启异步    // setTimeout(() => {        // resolve("ok");        reject("no");    // });});p.then(value => {    console.log(111);}, reason => {    // 如果返回值是非promise类型的数据    return reason;}).then(value => {    console.log(value); // "no"    // 如果返回值是promise类型的数据    return new Promise((resolve, reject) => {        resolve(333);    });}, reason => {    // ...}).then(value => {    console.log("收到的值是:", value); // 333    // 如果抛出异常    throw "error"}).then(() => {    // ...}, reason => {    console.log(reason); // error})// 如果then中是空值,什么也不传,出现bug!!!.then().then(() => {    console.log("我的上一个then是空值,我还可以输出吗?");});

最后的测试,如果then中是空值,什么也不传,出现bug!!!

4、值的穿透

解决:

Promise.prototype.then = function(onResolved, onRejected) {    // 注意保存一下this的指向,因为下面普通函数,内部的this指向是window    const _this = this;    // 判断回调函数参数    if (typeof onRejected !== "function") {        onRejected = reason => {            throw reason;        }    }    if (typeof onResolved !== "function") {        onResolved = value => value;    }    .....

当我们不在 then 中放入参数,例:promise.then().then(),那么其后面的 then 依旧可以得到之前 then 返回的值,这叫做值的穿透

4、完整代码示例

写这个then方法,被这个then方法折磨了一整天了,中途踩了一些巨坑。

关于Promise的手写教程就先写到这里吧,catch、all、race、resolve、reject等方法的实现,会在下一篇博客中进行讲解。

这些方法相对then方法来说就简单得多了。

如果关于本篇文章有什么问题的话,可以私信我,谢谢阅读。

function Promise(executor) {    // 添加属性    this.PromiseState = "pending";    this.PromiseResult = undefined;    // 用于保存回调函数的数组    this.callbacks = [];    // 保存实例对象的this值    const _this = this;    // 定义resolve函数    function resolve(data) {        // 判断状态,promise状态只能更改一次        if (_this.PromiseState !== "pending") return;        // 1、修改对象的状态(promiseState)        _this.PromiseState = "fulfilled"; // fulfilled        // 2、设置对象的结果值(promiseResult)        _this.PromiseResult = data;        // 3、调用异步任务下,成功的回调函数        setTimeout(() => {            _this.callbacks.forEach(item => {                item.Resolved(data);            });        });    }    function reject(data) {        // 判断状态,promise状态只能更改一次        if (_this.PromiseState !== "pending") return;        // 1、修改对象的状态(promiseState)        _this.PromiseState = "rejected"; // rejected        // 2、设置对象的结果值(promiseResult)        _this.PromiseResult = data;        // 3、调用异步任务下,失败的回调函数        setTimeout(() => {            _this.callbacks.forEach(item => {                item.Rejected(data);            });        });    }    try {        executor(resolve, reject);    } catch (e) {        reject(e);    }}Promise.prototype.then = function(onResolved, onRejected) {    // 注意保存一下this的指向,因为下面普通函数,内部的this指向是window    const _this = this;    // 判断回调函数参数    if (typeof onRejected !== "function") {        onRejected = reason => {            throw reason;        }    }    if (typeof onResolved !== "function") {        onResolved = value => value;    }    // then函数的返回值是一个新的promise对象    return new Promise((resolve, reject) => {        function handler(func) {            try {                let result = func(_this.PromiseResult);                // 如果返回值是promise对象                if (result instanceof Promise) {                    result.then(v => {                        resolve(v);                    }, r => {                        reject(r);                    });                } else {                    // 如果返回值是非promise对象                    resolve(result);                }            } catch (e) {                // 如果抛出异常,新promise变成rejected,reason为抛出的异常。                reject(e);            }        }        // 如果实例对象上的PromiseState是fulfilled,执行onResolved        if (this.PromiseState === "fulfilled") {            // 异步执行            setTimeout(() => {                handler(onResolved);            });        }        // 如果实例对象上的PromiseState是rejected,执行onRejected        if (this.PromiseState === "rejected") {            // 异步执行            setTimeout(() => {                handler(onRejected);            });        }        // 如果new Promise中的函数是异步的,例如一秒之后再调用resolveo或者reject的情况        // 后续的新promise对象的状态都是pending,都会走这里。        // 我们就将回调函数保存起来,在resolve和reject方法中去调用        if (this.PromiseState === "pending") {            this.callbacks.push({                Resolved: () => {                    handler(onResolved);                },                Rejected: () => {                    handler(onRejected);                }            });        }    });}

关键词: