从本文你将理解到
- 什么是promise
promise的外部实现
- resolve实例属性
- reject实例属性
- then办法
- then办法的屡次调用
- then的链式调用
- 谬误捕捉
try{}catch(e){}
- then可选参数
- 静态方法 all 的实现
- 静态方法 resolve 的实现
- 实例办法 finally 的实现
实例办法 catch 的实现
什么是promise
步骤剖析
/** * 1.Promise是个类,参数是个回调函数(执行器),这个执行器会立刻执行 * 2.promise中有三种状态,胜利fulfilled,失败rejected,期待pending,一旦状态确定,则不可更改 * pending -> fulfilled * pending -> rejected * 3.resolve,reject函数是用来扭转状态的 * resolve() pending -> fulfilled * reject() pending -> rejected * 4.then办法外部做的事件就是判断状态 如果状态是胜利调用胜利的回调函数,如果是失败调用失败的回调函数, * then办法是被定义在原型对象中的 * 5.then胜利的回调函数有个参数,示意胜利之后的值。then失败的回调函数有个参数,示意失败之后的起因*/new Promise((resolve,reject)=>{resolve("胜利")// reject("失败")})
实现
实现最根底的性能
申明一个 MyPromise 类,在构造函数中接管一个执行器,并立刻执行这个执行器
// 定义状态常量const PENDING = 'pending'; // 期待const FULFILLED = 'fulfilled'; // 胜利const REJECTED = 'rejected'; // 失败class MyPromise { constructor(executor) { // 执行器接管两个参数,resolve 和 reject // 执行器立刻执行 executor(this.resolve, this.reject); } // 定义实例的状态属性: 初始值为 pending,状态一旦确定,就不可更改 status = PENDING;}
执行 执行器 的时候,须要传递两个参数,resolve 和 reject。
- 在 执行器 函数体中执行这两个办法的时候是间接调用的
resolve(); reject()
- 间接执行函数的话,这个函数体外部的 this 会指向 window。
- 所以在定义 resolve 和 reject办法的时候应用箭头函数,防止 this 指向的问题,使其可能指向 promise 这个实例。
- 因为在 执行器 函数体中,resolve 和 reject 是用来更改 Promise 的状态的,而且更改之后,不能够再次更改
class MyPromise { // 构造函数执行器 ... resolve = () => { // 状态不是 pending 的时候,return。 if (this.status !== PENDING) return; this.status = FULFILLED; } reject = () => { // 状态不是 pending 的时候,return。 if (this.status !== PENDING) return; this.status = REJECTED; }}
调用 resolve
和 reject
的时候传入了参数:resolve('胜利后的数据')
reject('失败的起因')
所以在 resolve
和 reject
中接管对应的参数,并保留到实例属性中
class MyPromise { // 构造函数执行器 ... // 定义两个属性, // 胜利之后的值 value = undefined // 失败的起因 reason = undefined resolve = value => { // 接管胜利的数据 // 状态不是 pending 的时候,return。 if (this.status !== PENDING) return; this.status = FULFILLED; // 保留胜利的值 this.value = value; } reject = reason => { // 接管失败的起因 // 状态不是 pending 的时候,return。 if (this.status !== PENDING) return; this.status = REJECTED; // 保留失败的起因 this.reason = reason; }}
在调用 Promise()
之后,会返回一个 promise 实例,这个实例中有一个 then 办法,用于处理函数内完结的胜利或失败的回调
then
有两个参数,一个参数是胜利的回调successCallback
,一个是失败的回调failCallback
- 在调用
successCallback
的时候,须要将 胜利的数据/值 传递进去- 在调用
failCallback
的时候,须要将 谬误的起因 传递进去- 胜利的值 和 失败的起因曾经用定义的变量
value
和reason
保留过了。
要调用胜利的回调还是失败的回调,须要要看 Promise 的状态:
如果是 Fulfilled 调用胜利的回调;如果是 Rejected 调用失败的回调;
class MyPromise{ ... then(successCallback, failCallback) { if (this.status === FULFILLED) { successCallback(this.value); } else if (this.status === REJECTED) { failCallback(this.reason); } }}
退出 异步逻辑
如果在应用 Promise 的时候,把 resolve
或 reject
放在异步逻辑中。
// 2s 后再执行胜利的 resolvesetTimeout(() => {resolve('胜利')}, 2000)
这个时候,再应用 then
的话,then
外面判断 promise
状态,此时的 status
还是 padding
。
因为 promise 的状态 status
是靠调用 resolve/reject
来更改的。提早了 resolve/reject
的调用,就是提早了状态的更新。
而 then 办法中只判断了 胜利和失败 的状态,并没有去判断 期待的状态(也是就异步的状态)
then(successCallback, failCallback) { if (this.status === FULFILLED) { successCallback(this.value); } else if (this.status === REJECTED) { failCallback(this.reason); } }
所以咱们要做的事件是:如果在调用 then
的时候,promise
的状态是 pending
期待状态,须要将 传递进来的胜利的回调和失败的回调 存储起来,以便提早完结调用。
class MyPromise { ... // 胜利回调 successCallback = undefined; // 失败回调 failCallback = undefined; ... then(successCallback, failCallback) { if (this.status === FULFILLED) { successCallback(this.value); } else if (this.status === REJECTED) { failCallback(this.reason); } else { // 期待 // 保留胜利 和 失败的回调函数 this.successCallback = successCallback; this.failCallback = failCallback; } }}
之后在异步逻辑完结之后,会调用 resolve 或 reject ,此时就能够在Promise中的 resolve 或 reject 办法里判断是否有对应的回调,如果有就执行它
resolve = value => { ... // 判断是否有胜利的回调 this.successCallback && this.successCallback(this.value)}reject = reason => { ... // 判断是否有失败的回调 this.failCallback && this.failCallback(this.reason);}
实现 then 办法屡次调用 增加多个处理函数
- 每个 Promise 对象的 then 办法都是能够被屡次调用的,
- 当 then 办法被调用的时候,每个 then 办法外面的回调函数都是要被调用的
- 如果屡次调用了 then 办法,应该先将所有 then 办法外面的回调函数都存储起来,当 Promise 状态发生变化的时候,将顺次执行这些回调函数
为了存储多个 then 外面的回调函数,须要将
successCallback
和failCallback
属性改为数组// 胜利回调// successCallback = undefined;successCallback = [];// 失败回调// failCallback = undefined;failCallback = [];then() {... // 保留胜利 和 失败的回调函数 // this.successCallback = successCallback; // this.failCallback = failCallback; this.successCallback.push(successCallback); this.failCallback.push(failCallback);...}
而后在 Promise 状态更新的时候,顺次调用这些回调函数
resolve = value => { ... // 如果胜利回调存在,就执行它 // this.successCallback && this.successCallback(this.value); while (this.successCallback.length) { // 弹出第一个回调函数,并执行 this.successCallback.shift()(this.value); }}reject = reason => { ... // 如果失败回调存在,就执行它 // this.failCallback && this.failCallback(this.reason); while (this.failCallback.length) { // 弹出第一个回调函数,并执行 this.failCallback.shift()(this.reason); }}
应用
promise.then(value => {console.log(1);console.log(value);});promise.then(value => {console.log(2);console.log(value);});promise.then(value => {console.log(3);console.log(value);});
后果
1胜利2胜利3胜利
then 办法的链式调用
- Promise 的 then 办法是能够被链式调用的
- 每一个 then 办法中回调函数拿到的值,其实是上一个 then 办法回调函数的返回值
// test.jspromise .then(value => { console.log(value); return 100; }) .then(value => { // 这一个value 就是后面 then 办法回调函数中 return 的100 console.log(value); // 100 });
- 实现 then 办法的链式调用
每一个 then 办法都会返回一个 Promise 对象
then(successCallback, failCallback) { // 实现 then 办法返回一个 Promise 对象 let promise2 = new MyPromise(() => { if (this.status === FULFILLED) { successCallback(this.value); } else if (this.status === REJECTED) { failCallback(this.reason); } else { // 期待 // 保留胜利 和 失败的回调函数 // this.successCallback = successCallback; // this.failCallback = failCallback; this.successCallback.push(successCallback); this.failCallback.push(failCallback); } }); // 返回 Promise 对象 return promise2; }
- 将上一个 then 办法回调函数的返回值传递给下一个 then 办法回调函数
- 想要下一个then接管到上一个then,那么上一个then要返回一个prommise对象
获取回调函数的返回值
// 胜利的回调then(successCallback,failCallback){... let promise2 = new MyPromise(() => { if (this.status === FULFILLED) { // 保留胜利回调的返回值 let x = successCallback(this.value); }... }); return promise2}
将 x 传递给下一个 then。下一个 then 其实就是 promise2 外面的 then,(因为你返回了个promise2,他前面接的then必定是promise2的呀)
只有调用 promise2 的 resolve办法,那么promise2的then就会执行,同时要传递的 x
通过resolve(x)
就会传递给下一个 then 的回调函数了
// 胜利的回调then(successCallback,failCallback){ // 只有调用 resolve 办法,就能将 x 传递给 then 办法的回调函数 let promise2 = new MyPromise((resolve, reject) => { let x = successCallback(this.value); resolve(x); })}
then 办法链式调用返回值的判断
- 如果上一个 then 办法回调函数的返回值是一个 一般值,间接 resolve;
- 如果上一个 then 办法回调函数的返回值是一个 Promise 对象,则须要判断这个 Promise 对象的返回后果(状态),并依据状态来决定要应用 resolve 还是 reject
// 胜利的回调then(successCallback,failCallback){ let promise2 = new MyPromise((resolve, reject) => { let x = successCallback(this.value); // 判断 x 的值是一般值还是 Promise 对象, // 如果是一般值,间接调用 resolve // 如果是 Promise 对象,查看这个 Promise 对象的返回后果, // 再依据返回后果来决定调用 resolve 还是 reject resolveRromise(x, resolve, reject); })}function resolveRromise(x, resolve, reject) { // 应用 instanceof 来判断 x 是否是 MyPromise 的实例对象 if (x instanceof MyPromise) { // 调用 then 办法来查看 x 的返回值, // 如果是胜利的,就调用then的第一个参数办法,失败则调用then的第二个参数办法 // 简写成执行promise2对象的resolve和reject办法,向下传递 // x.then(value => resolve(value), reason => reject(reason)) x.then(resolve, reject); } else { resolve(x) }}
测试
function other (){ return new Promise((resolve,reject)=>{ setTimeout(()=>{ console.log("other") },2000) })}promise.then(value=>{ console.log(value) return other() //返回一个promise}).then(value=>{ console.log(value) //输入other})
then 办法链式调用辨认 Promise 对象自返回
如果 then 办法的回调函数中,返回了本人(Promise对象),Promise 就会进入循环调用,这种状况是不被容许的:Uncaught (in promise) TypeError: Chaining cycle detected for promise #<Promise>
var promise1 = new Promise((resolve, reject) => { resolve(100);});var promise2 = promise1.then(value => { console.log(value); // 100 // 本人返回本人 return promise2;})
解决办法:判断以后 then 的值 promise2 对象 和 它外部返回给下一个then 的promise对象的返回值 x
,是否雷同,如果雷同,就是 自反回。
...then(){ //传入promise2 在resolvePromise中将promise2和x进行判断 resolvePromise(promise2, x, resolve, reject);}...function resolvePromise(promise2, x, resolve, reject) { // 判断是否是 自反回 if (promise2 === x) { return reject(new TypeError('Chaining cycle detected for promise #<Promise>')) } // 应用 instanceof 来判断 x 是否是 MyPromise 对象 if (x instanceof MyPromise) { // 调用 then 办法来查看 x 的返回值, // 如果是胜利的,就调用resolve;失败就调用 reject // x.then(value => resolve(value), reason => reject(reason)) x.then(resolve, reject); } else { resolve(x) }}
问题:promise2 是new MyPromise((resolve, reject) => {...})
执行实现之后才有的,而当初是在 new promise2 执行的过程中去获取它,是获取不到的。
解决办法就是将 取到回调和比拟 promise2 和 x
的代码变为 异步执行。他会在then办法的所有同步代码执行完之后才执行,以便于取到promise2
// 将代码改为异步执行,确保拿到 promise2setTimeout(() => { // 保留胜利回调的返回值 let x = successCallback(this.value); // 判断 x 的值是一般值还是 Promise 对象, // 如果是一般值,间接调用 resolve // 如果是 Promise 对象,查看这个 Promise 对象的返回后果, // 再依据返回后果来决定调用 resolve 还是 reject // resolve(x); resolveRromise(promise2, x, resolve, reject);}, 0);
捕捉谬误及 then 链式调用其余状态代码补充
- 捕捉执行器的谬误
在执行执行器的时候,应用 try/catch 捕捉异样
当传入的执行器是个error不是失常的执行器时候获取e.message
class MyPromise {constructor(executor) { try { executor(this.resolve, this.reject); } catch (err) { this.reject(err); }}}
- 捕捉 then 办法回调函数执行异样
- 如果 then 办法的回调函数内产生异样,或者在then的回调函数内
throw new Error
则须要 reject 传递给下一个 then 办法的错误处理回调函数
try {// 保留胜利回调的返回值let x = successCallback(this.value);// 判断 x 的值是一般值还是 Promise 对象,// 如果是一般值,间接调用 resolve// 如果是 Promise 对象,查看这个 Promise 对象的返回后果,// 再依据返回后果来决定调用 resolve 还是 reject// resolve(x);resolveRromise(promise2, x, resolve, reject);} catch (err) {// 将异样传递给下一个 then 办法的错误处理回调函数,reject(err);}
- 解决 catch 链式调用
后面解决了 resolve 的链式调用状况。而 reject 的解决同理。
...else if (this.status === REJECTED) { // failCallback(this.reason); // 将代码改为异步执行,确保可能在 new Promise 执行完之后拿到 promise2 setTimeout(() => { try { // 保留失败回调的返回值 let x = failCallback(this.reason); // 判断 x 的值是一般值还是 Promise 对象, // 如果是一般值,间接调用 resolve // 如果是 Promise 对象,查看这个 Promise 对象的返回后果, // 再依据返回后果来决定调用 resolve 还是 reject // resolve(x); resolveRromise(promise2, x, resolve, reject); } catch (err) { // 将异样传递给下一个 then 办法的错误处理回调函数, reject(err); } }, 0);}...
!!!留神:
如果在错误处理回调函数中返回了一个正常值(比方: 10000),它就会进入下一个 then 办法的胜利解决回调函数。
如果返回的是失败的,它就会进入下一个 then 的错误处理回调函数中
const promise = new MyPromise((resolve, reject) => { reject('失败');});promise .then(value => { console.log(value); return 100; }, reason => { console.log(reason); // => 第一个then接管到失败,然而返回了个胜利10000 return 10000; }) .then(value => { console.log(value); // => 10000 }, reason => { console.log(reason); });
- 解决 异步逻辑 的链式调用,并捕捉运行异样
...else { // 期待 // 保留胜利 和 失败的回调函数 // this.successCallback = successCallback; // this.failCallback = failCallback; // this.successCallback.push(successCallback); // this.failCallback.push(failCallback); // 保留一个函数,这个函数外面执行 胜利/失败的回调 this.successCallback.push(() => { setTimeout(() => { try { // 保留胜利回调的返回值 let x = successCallback(this.value); // 判断 x 的值是一般值还是 Promise 对象, // 如果是一般值,间接调用 resolve // 如果是 Promise 对象,查看这个 Promise 对象的返回后果, // 再依据返回后果来决定调用 resolve 还是 reject // resolve(x); resolveRromise(promise2, x, resolve, reject); } catch (err) { // 将异样传递给下一个 then 办法的错误处理回调函数, reject(err); } }, 0); }); this.failCallback.push(() => { setTimeout(() => { try { // 保留失败回调的返回值 let x = failCallback(this.reason); // 判断 x 的值是一般值还是 Promise 对象, // 如果是一般值,间接调用 resolve // 如果是 Promise 对象,查看这个 Promise 对象的返回后果, // 再依据返回后果来决定调用 resolve 还是 reject // resolve(x); resolveRromise(promise2, x, resolve, reject); } catch (err) { // 将异样传递给下一个 then 办法的错误处理回调函数, reject(err); } }, 0); });}
通过下面的解决,再执行回到函数的时候就无需传值了.
...this.successCallback.shift()();...this.failCallback.shift()();
将 then 办法变为可选参数
Promise 的 then 办法的参数是可选的,如果调用 then 办法的时候,不传递参数,这种状况下,Promise 的流程要怎么走呢?
const promise = new MyPromise((resolve, reject) => { resolve(100);});promies .then() .then() .then(value => console.log(value)) // 100
在 then 办法不传递参数的时候,其状况等同于这样的
promise .then(value => value) // 接管到参数,并将这个参数间接返回
这样的话,promise状态就会传递到下一个 then 办法
then(successCallback, failCallback) { // 判断是否有传递参数进来 successCallback = successCallback ? successCallback : value => value; failCallback = failCallback ? failCallback : reason => { throw reason }; ... }
Promise.all 办法
应用 Promise.all 办法能够依照程序去执行异步工作,并失去程序的后果
const p1 = function() {return Promise(resolve => { setTimeout(() => { resolve('p1'); }, 2000)})}const p2 = function() {return new Promise(resolve => { resolve('p2');})}Promise.all(['a', 'b', p1(), p2(), 'c']).then(results => {// ['a', 'b', 'p1', 'p2', 'c']})
2s 后输入
['a', 'b', 'p1', 'p2', 'c']
,- Promise.all 办法接管一个数组,外部会依照数组元素的程序执行,全副执行完之后,再将后果一起返回,返回的后果也是数组,与传入的数组绝对应。
- 如果有一个失败了。返回的后果就是失败的。
// 静态方法,能够间接应用 MyPromise.all // 接管一个数组 static all(array) { // 定义 result,用来存储后果 const result = []; // 记录曾经执行结束的数组元素个数 let index = 0; // 返回一个 promise 对象 return new MyPromise((resolve, reject) => { // 将数据增加到指标数据中 function addRes(i, value) { result[i] = value; // 执行结束+1 index++; // 如果数组中所有元素都执行结束,才 resolve 后果 if (index === array.length) resolve(result); } // 遍历数组,并判断每个数组元素是一般值还是 Promise 对象, // 如果是一般值,间接将元素放到后果集中, // 如果是Promise 对象,须要获取其返回值,而后放到后果集中 for (let i = 0; i < array.length; i++) { let current = array[i]; // 判断是否是 promise 对象 if (current instanceof MyPromise) { current.then(value => addRes(i, value), reason => reject(reason)) } else { addRes(i, current); } } }) }
Promise.resolve 办法
- Promise.resolve 办法 能够将给定的一个值转化为一个 Promise 对象。
Promise.resolve 对象返回的就是一个 Promise 对象,这个对象包裹着给定的这个值。
Promise.resolve(10).then(value => console.log(value)); // 10
- 如果传入的是一个Promise 对象,它会一成不变的将这个 Promise 对象返回
function p1 () { return new Promise((resolve, reject) => { resolve(100) })}Promise.resolve(p1()).then(value => console.log(value)); // 100
// resolve 静态方法static resolve(value) { // 如果value 是Promise 对象,间接返回value if (value instanceof MyPromise) return value; // 如果是一般值,返回 Promise 对象,并将value 包裹 return new MyPromise(resolve => resolve(value));}
finally
- Promise 的状态不论胜利还是失败的,finally 的回调函数都会被调用
- 非类办法,接管一个回调函数,无论promise对象最终是胜利还是失败,finally办法的回调函数始终会被执行一次
- 能够链式调用then,能够拿到以后这个promise对象返回的后果
finally 办法回调函数中 return 能够返回 Promise对象,后续 then 办法的回调函数应该期待 这个 Promise 执行完能力持续进行
finally(callback){ return this.then(value=>{ //then返回一个promise对象 callback() //无论成功失败都会执行 return value; //传递给下一个then},reason=>{ callback() throw reason;})}
因为callback也能够返回promise,所以用resolve革新一下
留神返回的value和reason是上面例子中promise的value,不是callback中p1这个promise的返回值
finally(callback){ return this.then(value=>{ return MyPromise.resolve(callback()).then(()=>value) //留神value为链式调用的value不是callback办法里的value },reason=>{ return MyPromise.resolve(callback()).then(()=>{throw reason}) //留神reason })}
调用
const promise = new MyPromise((resolve, reject) => { reject('失败');});const p1 = function () { return new MyPromise(resolve => { setTimeout(() => { resolve('2s的异步逻辑'); }, 2000); })}promise.finally(() => { console.log('finally'); return p1(); // return Promise 对象}).then(value => { console.log(value);}, reason => { console.log(reason);})
finally失败
catch 办法
- catch 办法是用来解决以后 Promise 对象最终状态为失败的状况的。
- 应用它,咱们能够不必在 then 办法中传递失败的回调。
只须要在 catch 办法外部去调用 then 办法即可,而后将 then 办法的胜利回调传入 undefined,失败的传入一个回调函数
catch(failCallback) { return this.then(undefined, failCallback); }
测试
promise.finally(() => { console.log('finally'); return p1(); // return Promise 对象}).then(value => { console.log(value);}).catch(reason => { console.log('catch', reason)})
finallycatch 失败