回顾用法
let promise = new Promise((resolve, reject) => {resolve('成功')resolve('reject')})promise.then(value => {}, reason => {})
Promise就是一个类 在执行这个类的时候,需要传递一个执行器进去 执行器会立即执行Promise有三种状态 分别为成功fulfilled、 失败rejected、 等待pending。- 一旦状态确定就不可更改
- pending -> fulfilled
- pending -> rejected
- resolve 和 rejected 函数是用来更改状态的
- resolve : fulfilled
- reject : rejected
- then方法内部做的事情就判断状态 如果状态是成功 调用成功的回调函数 如果状态是失败 调用失败回调函数 then方法是被定义在原型对象中的
- then成功回调有一个参数 表示成功之后的值 then失败回调有一个参数 表示失败后的原因
核心逻辑
定义状态枚举
把三个状态定义为枚举。 便于以后的非字符串输入引起错误
const PENDING = "pending";const RESOLVED = "resolved";const REJECTED = "rejected";
定义一个类
在回顾用法中知道,new promise 过程中会传入
class MyPromise {constructor(executor) {executor(this.resolve, this.reject);}}
状态更改
状态只允许更改一次。,要做非等待状态的判断
修改对应的状态 ```javascript class MyPromise { constructor(executor) { executor(this.resolve, this.reject); } status = PENDING;
resolve = () => { // 状态只允许更改一次。 if (this.status !== PENDING) return; // 将状态更改为成功 this.status = RESOLVED; };
reject = () => { if (this.status !== PENDING) return; // 将状态更改为失败 this.status = REJECTED; }; }
<a name="IQyoh"></a>### then- 会有成功 、 失败的回调- 回调会有原因, 因此在改变状态的时候需要保存原因,在回调的时候返回```javascriptclass MyPromise {constructor(executor) {executor(this.resolve, this.reject);}status = PENDING;value = undefinedreason = undefinedresolve = value => {if (this.status !== PENDING) return;this.status = RESOLVED;// 1 保存成功原因this.value = value};reject = reason => {if (this.status !== PENDING) return;this.status = REJECTED;// 1 保存失败原因this.reason = reason};then(successCallback, failCallback) {// 2 判断状态if(this.status === RESOLVED) {successCallback(this.value)} else if(this.status === REJECTED){failCallback(this.reason)}}}
异步逻辑
eg: 异步回调。
let promise = new Promise((resolve, reject) => {setTimeout(() => {resolve("成功");reject('失败')}, 20000)});promise.then((value) => {console.log("----value", value);},(reason) => {console.log("----reason", reason);});
此时按照目前的逻辑,是不会执行 successCallback 或 failCallback。
改造:
then中判断是否是等待状态,如果是,保存 成功和失败 的callback在resolve 、reject中判断 这个回调是否存在,存在并把对应参数带回去调用。
class MyPromise {constructor(executor) {executor(this.resolve, this.reject);}status = PENDING;value = undefined;reason = undefined;// 1 定义两个callback变量successCallback = undefined;failCallback = undefined;resolve = (value) => {if (this.status !== PENDING) return;this.status = RESOLVED;this.value = value;// 2 如果有callback则调用,并把对应参数带过去this.successCallback && this.successCallback(this.value)};reject = (reason) => {if (this.status !== PENDING) return;this.status = REJECTED;this.reason = reason;// 2 如果有callback则调用,并把对应参数带过去this.failCallback && this.failCallback(this.reason)};then(successCallback, failCallback) {if (this.status === RESOLVED) {successCallback(this.value);} else if (this.status === REJECTED) {failCallback(this.reason);} else {// 3 如果还是等待状态,保存callbackthis.successCallback = successCallback;this.failCallback = failCallback;}}}
then 方法多次调用添加多个处理函数
同一个promise对象下面的then方法是可以被调用多次的。
eg: ```javascript let promise = new MyPromise((resolve, reject) => { setTimeout(() => { resolve(“成功”); reject(“失败”); }, 2000); // resolve(“成功”); // reject(“失败”); }); promise.then( (value) => { console.log(“——value”, value); }, (reason) => { console.log(“——reason”, reason); } );
promise.then((value) => { console.log(1, value); }); promise.then((value) => { console.log(2, value); }); promise.then((value) => { console.log(3, value); });
那么在此时,目前的代码只会保存最后一次的 `callback`, 因此我们需要- 将`callback` 改造成数组```javascriptclass MyPromise {constructor(executor) {executor(this.resolve, this.reject);}status = PENDING;value = undefined;reason = undefined;// 1 变成数组successCallback = [];failCallback = []resolve = (value) => {if (this.status !== PENDING) return;this.status = RESOLVED;this.value = value;// 2 while 判断长度,shift 不断推出第一个元素,将this.value传进去while(this.successCallback.length) this.successCallback.shift()(this.value)};reject = (reason) => {if (this.status !== PENDING) return;this.status = REJECTED;this.reason = reason;// 2 while 判断长度,shift 不断推出第一个元素,将this.value传进去while(this.failCallback.length) this.failCallback.shift()(this.reason)};then(successCallback, failCallback) {if (this.status === RESOLVED) {successCallback(this.value);} else if (this.status === REJECTED) {failCallback(this.reason);} else {// 3 变成pushthis.successCallback.push(successCallback);this.failCallback.push(failCallback);}}}
链式调用
then方法是可以被链式调用的, 后面then方法的回调函数拿到值的是上一个then方法的回调函数的返回值
普通值
let promise = new Promise((resolve, reject) => {resolve("成功");});promise.then((value) => {console.log(1, value);return 100}).then(value => {console.log(value);})
输出结果如下
因此我们需要
- 返回新的 promise
- successCallback 返回的东西也需要带给下一个 .then
以前then 需要立即执行,放哪? -> 放入 new Promise 中,立即执行
then(successCallback, failCallback) {// 定义为一个新的Promiselet promise2 = new MyPromise((resolve, reject) => {if (this.status === RESOLVED) {// 将当前successCallback return 的值返回let x = successCallback(this.value);resolve(x)} else if (this.status === REJECTED) {failCallback(this.reason);} else {this.successCallback.push(successCallback);this.failCallback.push(failCallback);}});// 将新的Promise返回,以完成链式调用return promise2;}
callback 返回的又是一个Promise
特俗的, 如果返回的又是一个Promise, 那么我们需要判断,Promise的状态是成功 的还是失败的,如果是成功,则调用
resolve, 如果是 失败,则要reject。
需要处理逻辑的如下判断 x 的值是普通值还是promise对象
- 如果是普通值 直接调用resolve
- 如果是promise对象 查看promsie对象返回的结果
- 再根据promise对象返回的结果 决定调用resolve 还是调用reject
我们定义一个新方法resolvePromise(不是类中的方法,定义在类之外),便于统一调用。 传递的参数分别是 callback的值,resolve, reject
function resolvePromise (x, resolve, reject) {//判断 x 的值是否 promise 对象if (x instanceof MyPromise) {x.then(resolve, reject);} else {// 普通值resolve(x);}}
then 中修改如下
then(successCallback, failCallback) {let promise2 = new MyPromise((resolve, reject) => {if (this.status === RESOLVED) {let x = successCallback(this.value);// 调用resolvePromiseresolvePromise(x, resolve, reject)} else if (this.status === REJECTED) {failCallback(this.reason);} else {this.successCallback.push(successCallback);this.failCallback.push(failCallback);}});return promise2;}
特殊循环调用
在特殊情况下,可以让return的promise 为自身,这就会导致循环调用。
eg:
let promise = new Promise((resolve, reject) => {resolve("成功");});let promise2 = promise.then((value) => {console.log(1, value);return promise2;})
结果如下
因此我们需要判断 return 的内容是否为 自身。
修改如下
class MyPromise {....then(successCallback, failCallback) {let promise2 = new MyPromise((resolve, reject) => {if (this.status === RESOLVED) {let x = successCallback(this.value);// 将promise2 自身传进去做判断resolvePromise(promise2, x, resolve, reject)} else if (this.status === REJECTED) {failCallback(this.reason);} else {this.successCallback.push(successCallback);this.failCallback.push(failCallback);}});return promise2;}}function resolvePromise (promise2, x, resolve, reject) {// 加上判断是否为自身, 如果是reject一个错误。if(promise2 === x) {return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))}if (x instanceof MyPromise) {x.then(resolve, reject);} else {resolve(x);}}
但这由有一个新的问题,在resolvePromise传值的时候,promise 是还没有拿到的。 我们需要怎么做呢
- 处理成异步
因此我们只要加个settime即可。
class MyPromise {。。。then(successCallback, failCallback) {let promise2 = new MyPromise((resolve, reject) => {if (this.status === RESOLVED) {// 添加setTimeout, 使其异步化setTimeout(() => {let x = successCallback(this.value);resolvePromise(promise2, x, resolve, reject);}, 0);} else if (this.status === REJECTED) {failCallback(this.reason);} else {this.successCallback.push(successCallback);this.failCallback.push(failCallback);}});return promise2;}}
捕获错误
截至至目前,我们都在处理正常的情况,我们需要添加相应的错误兼容,已保证代码的正常执行。
添加try catch
class MyPromise {constructor(executor) {// 添加try catchtry {executor(this.resolve, this.reject);} catch (error) {// 将错误reject 出去this.reject(error)}}....then(successCallback, failCallback) {let promise2 = new MyPromise((resolve, reject) => {if (this.status === RESOLVED) {setTimeout(() => {// 添加try catchtry {let x = successCallback(this.value);resolvePromise(promise2, x, resolve, reject);} catch (error) {reject(error)}}, 0);} else if (this.status === REJECTED) {failCallback(this.reason);} else {this.successCallback.push(successCallback);this.failCallback.push(failCallback);}});return promise2;}}
补充其他状态的处理
目前,我们只是处理的
resolve的状态,我们继续添加reject 状态的代码class MyPromise {...then(successCallback, failCallback) {let promise2 = new MyPromise((resolve, reject) => {if (this.status === RESOLVED) {setTimeout(() => {try {let x = successCallback(this.value);resolvePromise(promise2, x, resolve, reject);} catch (error) {reject(error)}}, 0);} else if (this.status === REJECTED) {setTimeout(() => {// 添加 try catchtry {let x = failCallback(this.reason)resolvePromise(promise2, x, resolve, reject);} catch (error) {reject(error)}}, 0);} else {this.successCallback.push(successCallback);this.failCallback.push(failCallback);}});return promise2;}}
异步处理
结合前面成功 和 失败的 处理, 我们还需要对异步的回调函数处理
class MyPromise {constructor(executor) {try {executor(this.resolve, this.reject);} catch (error) {this.reject(error)}}status = PENDING;value = undefined;reason = undefined;successCallback = [];failCallback = [];resolve = (value) => {if (this.status !== PENDING) return;this.status = RESOLVED;this.value = value;// 不再需要传值// while (this.successCallback.length) this.successCallback.shift()(this.value);while (this.successCallback.length) this.successCallback.shift()();};reject = (reason) => {if (this.status !== PENDING) return;this.status = REJECTED;this.reason = reason;// 不再需要传值// while (this.failCallback.length) this.failCallback.shift()(this.reason);while (this.failCallback.length) this.failCallback.shift()();};then(successCallback, failCallback) {let promise2 = new MyPromise((resolve, reject) => {if (this.status === RESOLVED) {setTimeout(() => {try {let x = successCallback(this.value);resolvePromise(promise2, x, resolve, reject);} catch (error) {reject(error)}}, 0);} else if (this.status === REJECTED) {setTimeout(() => {try {let x = failCallback(this.reason)resolvePromise(promise2, x, resolve, reject);} catch (error) {reject(error)}}, 0);} else {this.successCallback.push(() => {// 将成功的函数copy一份放在此setTimeout(() => {try {let x = successCallback(this.value);resolvePromise(promise2, x, resolve, reject);} catch (error) {reject(error)}}, 0);});this.failCallback.push(() => {// 将失败的函数copy一份放在此setTimeout(() => {try {let x = failCallback(this.reason)resolvePromise(promise2, x, resolve, reject);} catch (error) {reject(error)}}, 0);});}});return promise2;}}
将 then 方法的参数变成可选参数
我们先来看一段代码 ```javascript let promise = new Promise((resolve, reject) => { resolve(“成功”); // reject(“失败”); });
promise .then() .then() .then((value) => { console.log(value); });
再 `then` 中,我们可以选择不传递参数,在最后有参数的地方,拿到一层层传递下来的参数。<br />实际上,上面的代码 等同于 下面的代码```javascriptpromise.then(value => value).then(value => value).then((value) => {console.log(value);});
因此
判断是否有callback, 有则用,没有则补充
class MyPromise {...then(successCallback, failCallback) {// 添加可选参数successCallback = successCallback || (value => value);// 失败的回调需加 throwfailCallback = failCallback || (reason => { throw reason });let promise2 = new MyPromise((resolve, reject) => {...}return promise2;}}
静态方法
all
Promise 允许我们将多个promise 执行完后一次性返回, 这就是 all 方法。注意的是,这是个静态方法。以下代码是例子。 特殊的,有一个返回的是
reject, 则其他都不会返回 ```javascript let p1 = new Promise((resolve, reject) => { setTimeout(() => { resolve(“成功 p1”); }, 2000) });
let p2 = new Promise((resolve, reject) => { resolve(“成功 p2”); });
let p = Promise.all([‘a1’, ‘a2’, p1, p2, ‘c1’]) p.then(res => console.log(res))
由以上代码可以得知,- `all` 传递的是个数组- 传递元素可以是非promise实例实现代码```javascriptclass MyPromise {...// 定义静态方法allstatic all(arr) {// 定义一个数组保存结果let result = [];// 保存一个index 和 数组长度判断是否执行let index = 0;return new MyPromise((resolve, reject) => {function addData(key, value) {// 保存结果值result[key] = value;// 每次+1index++;// 判断是否执行if (index === arr.length) {resolve(result);}}for (let idx = 0; idx < arr.length; idx++) {const current = arr[idx];// 判断是否是MyPromise 实例下的if (current instanceof MyPromise) {// 将then 中的结果放入 结果数组current.then((value) => addData(idx, value),(reason) => reject(reason));} else {// 如果不是直接add进结果数组addData(idx, current);}}});}}
resolve
resolve 可以将传进来的参数变成一个promise. 让后续也可以链式调用。 例子如下
function p1() {return new Promise((resolve, reject) => {setTimeout(() => {resolve("成功 p1");}, 2000);});}Promise.resolve(10).then((res) => console.log(res));Promise.resolve(p1()).then((res) => console.log(res));
log如下
其实很简单。
- 判断是否是
Promise实例,如果是直接 返回 不是
resolve出去class MyPromise {...// 定义静态方法resolvestatic resolve (value) {// 如果是Promise实例,直接返回if (value instanceof MyPromise) return value;// 不是则resolve 出去return new MyPromise(resolve => resolve(value));}}
finally
我们知道
finally, 无论如何都可以调用 。我们先来复习看下例子是如何用的
let p1 = new Promise((resolve, reject) => {console.log('.....');resolve("成功 p1");});p1.then((res) => console.log(res)).finally(() => {console.log("----1");});p1.finally(() => {console.log("----2");}).then((res) => console.log(res))
log 如下
我们可以发现
- finally 可以放在如何位置 按顺序执行
- finally 放前面 后面接 then 是可以拿到 返回的值
- 需要注意的是,这里有微任务宏任务的概念。 then 后面接 finally 或者 finally 后面接then 都是第二次微任务。
- 需要考虑finally 在前面,返回的value 是异步的情况。
代码如下
class MyPromise {...finally(callback) {// 因为可以链式调用,我们需要返回一个promise, 直接调用.then 方法既可。return this.then(value => {// 如果 return 的是个promise 需要等待执行完再returnreturn MyPromise.resolve(callback()).then(() => value)}, reason => {return MyPromise.resolve(callback()).then(() => { throw reason })})}}
catch
话不多说,先看例子
let p1 = new Promise((resolve, reject) => {reject('失败回调')});p1.then((res) => console.log(res)).catch(err => {console.log(err);})
log 如下;
我们可以发现
then不传递 失败回调时,是可以在 catch 中捕获到的。
其实实现很简单,只需要将callback放入then(undefind, callback ) 既可
代码实现如下
class MyPromise {...catch (failCallback) {return this.then(undefined, failCallback)}}
结束
自此,已完成全部逻辑。下面是代码总览
const PENDING = 'pending'; // 等待const FULFILLED = 'fulfilled'; // 成功const REJECTED = 'rejected'; // 失败class MyPromise {constructor (executor) {try {executor(this.resolve, this.reject)} catch (e) {this.reject(e);}}// promsie 状态status = PENDING;// 成功之后的值value = undefined;// 失败后的原因reason = undefined;// 成功回调successCallback = [];// 失败回调failCallback = [];resolve = value => {// 如果状态不是等待 阻止程序向下执行if (this.status !== PENDING) return;// 将状态更改为成功this.status = FULFILLED;// 保存成功之后的值this.value = value;// 判断成功回调是否存在 如果存在 调用while(this.successCallback.length) this.successCallback.shift()()}reject = reason => {// 如果状态不是等待 阻止程序向下执行if (this.status !== PENDING) return;// 将状态更改为失败this.status = REJECTED;// 保存失败后的原因this.reason = reason;// 判断失败回调是否存在 如果存在 调用while(this.failCallback.length) this.failCallback.shift()()}then (successCallback, failCallback) {// 添加可选参数successCallback = successCallback || (value => value);// 失败的回调需加 throwfailCallback = failCallback || (reason => { throw reason });let promsie2 = new MyPromise((resolve, reject) => {// 判断状态if (this.status === FULFILLED) {setTimeout(() => {// 添加容错try {let x = successCallback(this.value);resolvePromise(promsie2, x, resolve, reject)}catch (e) {reject(e);}}, 0)}else if (this.status === REJECTED) {setTimeout(() => {// 添加容错try {let x = failCallback(this.reason);resolvePromise(promsie2, x, resolve, reject)}catch (e) {reject(e);}}, 0)} else {// 等待// 将成功回调和失败回调存储起来this.successCallback.push(() => {setTimeout(() => {try {let x = successCallback(this.value);resolvePromise(promsie2, x, resolve, reject)}catch (e) {reject(e);}}, 0)});this.failCallback.push(() => {setTimeout(() => {try {let x = failCallback(this.reason);resolvePromise(promsie2, x, resolve, reject)}catch (e) {reject(e);}}, 0)});}});return promsie2;}finally(callback) {// 因为可以链式调用,我们需要返回一个promise, 直接调用.then 方法既可。return this.then(value => {// 如果 return 的是个promise 需要等待执行完再returnreturn MyPromise.resolve(callback()).then(() => value)}, reason => {return MyPromise.resolve(callback()).then(() => { throw reason })})}catch (failCallback) {return this.then(undefined, failCallback)}// 定义静态方法allstatic all(arr) {// 定义一个数组保存结果let result = [];// 保存一个index 和 数组长度判断是否执行let index = 0;return new MyPromise((resolve, reject) => {function addData(key, value) {// 保存结果值result[key] = value;// 每次+1index++;// 判断是否执行if (index === arr.length) {resolve(result);}}for (let idx = 0; idx < arr.length; idx++) {const current = arr[idx];// 判断是否是MyPromise 实例下的if (current instanceof MyPromise) {// 将then 中的结果放入 结果数组current.then((value) => addData(idx, value),(reason) => reject(reason));} else {// 如果不是直接add进结果数组addData(idx, current);}}});}// 定义静态方法resolvestatic resolve (value) {// 如果是Promise实例,直接返回if (value instanceof MyPromise) return value;// 不是则resolve 出去return new MyPromise(resolve => resolve(value));}}// 判断 x 的值是普通值还是promise对象// 如果是普通值 直接调用resolve// 如果是promise对象 查看promsie对象返回的结果// 再根据promise对象返回的结果 决定调用resolve 还是调用rejectfunction resolvePromise (promsie2, x, resolve, reject) {if (promsie2 === x) {return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))}if (x instanceof MyPromise) {// promise 对象// x.then(value => resolve(value), reason => reject(reason));x.then(resolve, reject);} else {// 普通值resolve(x);}}
