Promise解决了什么问题
- Promise解决了回调地狱的问题,一层一层嵌套的代码非常不美观,它带来了两个问题,可读性问题和信任问题
- 信任问题,在传统的异步编程中,往往会出现回调过早、回调过晚或者没有回调、回调次数过多等等一些问题
- Promise最大的好处是在异步执行的流程中,把执行代码和处理结果的代码清晰地分离了,Promise 将回调嵌套改成了链式调用,增加了可读性和信任问题
看一个最简单的Promise的使用
const p1 = new Promise((resolve, reject) => {
resolve('success')
})
p1.then((res) => console.log('res'), err => console.log(err))
// success
Promise核心
- promise就是一个类,在执行这个类的时候,需要传递一个执行器
executor()
,执行器会立即执行 -
executor
接受两个参数,分别是 resolve 和 reject - promise中有三种状态,分别为 成功
fulfilled
,失败rejected
, 等待pending
, - promise 只能从
pending
到rejected
, 或者从pending
到fulfilled
, - promise 的状态一旦确认,就不会再改变
- then方法内部做的事情就是判断状态,如果状态是成功,调用成功的回调函数,如果状态是失败,调用失败的函数,then方法是被定义在原型对象上的
- promise 可以then多次,
promise
的then 方法返回一个promise
- then 的参数
onFulfilled
和onRejected
是可选的 - then成功回调有一个参数,表示
成功
之后的值,then失败
回调有一个参数,表示失败后的原因
我们可以写出Promise的大致结构
const PENDING = 'pending'; // 等待
const FULFILLED = 'fulfilled'; // 成功
const REJECTED = 'rejected'; // 失败
class MyPromise {
constructor (executor) {
executor(this.resolve, this.reject)
}
// promise 状态
status = PENDING;
// 成功后的值
value = undefined;
// 失败后的值
reason = undefined;
resolve = () => {
// 如果状态不是等待,阻止程序向下运行
if (this.status !== PENDING) return;
// 将状态更改为成功
this.status = FULFILLED;
// 保存成功之后的值
this.value = value;
}
reject = () => {
// 如果状态不是等待,阻止程序向下运行
if (this.status !== PENDING) return;
// 将状态更改为失败
this.status = REJECTED;
// 保存失败后的原因
this.reason = reason;
}
then (successCallback, failCallback) {
// 判断状态
if (this.status === FULFILLED) {
successCallback();
} else if (this.status === REJECTED) {
failCallback()
}
}
}
测试一下
let p1 = new MyPromise((resolve, reject) => {
resolve('成功')
})
p1.then((value) => {
console.log(value)
}, reason => {
console.log(reason)
})
// 成功
Promise A+规范
根据规范我们可以总结几条核心的规则,更多的规范我们可以查看文档进行总结~
- 一个
Promise
的当前状态必须为以下三种状态中的一种:等待态(Pending)
、执行态(Fulfilled)
和拒绝态(Rejected)
。 - Promise 的 then 方法接受两个参数
promise.then(onFulfiled, onRejected)
- then 方法可以被同一个 Promise 调用多次
- then 方法必须返回一个 Promise 对象
下面我们将一步步来实现Promise
Promise中加入异步逻辑
// 成功回调
successCallback = undefined;
// 失败后的回调
faliCallback = undefined;
resolve = value => {
// 判断成功回调是否存在,如果存在 调用
this.successCallback && this.successCallback(this.value)
}
reject = reason => {
// 判断失败回调是否存在,如果存在则调用
this.faliCallback && this.faliCallback(this.reason)
}
then (successCallback, faliCallback) {
// 判断状态
if (this.status === FULFILLED) {
successCallback(this.value)
} else if (this.status === REJECTED) {
faliCallback(this.reason);
} else {
// 等待
// 将等待回调和失败回调存储起来
this.successCallback = successCallback;
this.faliCallback = faliCallback;
}
}
测试一下
let promise = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve('success')
}, 2000)
})
promise.then(value => {
console.log(value)
}, reason => {
console.log(reason)
})
// success
实现then 方法多次调用添加多个处理函数
- 分同步和异步情况处理
- 如果是同步直接调用回调函数,如果是异步,需要将回调函数存储起来
- 首先把成功回调和失败回调的值变成数组
- 接着将成功回调和失败回调使用push方法把成功回调push进数组
- 当promise的状态变成成功或者失败时,我们需要依次调用数组中的回调函数
- 当成功回调的数组长度不等于0,就有回调函数,while 循环从前往后执行调用shift()方法,每执行一个删除一个,直到数组长度为0
successCallback = [];
failCallback = [];
// 将原来代码修改为
// this.successCallback && this.successCallback(this.value);
while (this.successCallback.length) this.successCallback.shift()(this.value);
// this.faliCallback && this.faliCallback(this.reason);
while (this.faliCallback.length) this.faliCallback.shift()(this.reason);
测试一下
let promise = new MyPromise((resolve, reject) => {
// resolve('大白菜~~')
// reject('失败')
setTimeout(() => {
resolve('大白菜~')
}, 2000)
})
promise.then(value => {
console.log(value)
}, reason => {
console.log(reason)
})
// 大白菜
实现then方法链式调用
-
Promise
的then
方法是可以链式调用的,后面的then
方法回调函数拿到的值实际上是拿到上一个then
方法回调函数的返回值 - 实现
then
方法的链式调用,then
方法是Promise
对象下面的,如果要实现链式调用,那么每一个then
方法都应该返回一个Promise
对象
注意
- 在链式调用
then
方法的时候,回调函数可以返回一个普通值,和一个promise
对象 - 如果返回的是普通值,我们可以直接调用
resolve(x)
把这个普通值传递给下一个promise
对象 - 如果是
promise
对象的话,我们需要查看返回的promise
对象状态,如果状态是成功的,我们需要调用resolve
方法,把成功的状态传递给它,
如果是失败的,需要把reject
传递给下一个promise
对象
// 改造then方法
then (successCallback, faliCallback) {
let promise2 = new myPromise((resolve, reject) => {
// 判断状态
if (this.status === FULFILLED) {
let x = successCallback(this.value);
// 判断x的是是普通值还是promise对象
// 如果是普通值, 直接调用resolve
// 如是是promise对象 查看promise对象返回的结果
// 在根据promise对象返回的结果 决定调用resovle还是reject
resolvePromise(x, resolve, reject)
// resolve(x)
} else if (this.status === REJECTED) {
faliCallback(this.reason);
} else {
// 等待
// 将等待回调和失败回调存储起来
this.successCallback.push(successCallback);
this.faliCallback.push(faliCallback);
}
});
return promise2;
}
// 定义resolvePromise方法
function resolvePromise(x, resolve, reject) {
if (x instanceof myPromise) {
// promise对象
x.then(resolve, reject);
} else {
// 普通值
}
}
// 测试一下
let promise = new MyPromise((resolve, reject) => {
resolve('大白菜~~')
// reject('失败')
// setTimeout(() => {
// resolve('大白菜~')
// }, 2000)
})
function other () {
return new MyPromise((resolve, reject) => {
resolve('other');
})
}
promise.then(value => {
console.log(value);
return other();
}).then(value => {
console.log(value)
})
// 大白菜
// other
Promise then方法链式调用识别Promise对象自动返回
当链式调用Promise
对象下面的then
方法的时候, 在then
方法回调函数中可以返回Promise
对象,但我们需要考虑另外一种情况,在then
方法回调函数中不能返回当前这个then
方法他所返回的Promise
对象, 如果返回了then
方法返回的Promise
对象,就会发生循环调用。
示例
let promise = new Promise((resolve, reject) => {
resolve('大白菜')
})
let p1 = promise.then((value) => {
console.log(value)
return p1
})
// 报错
// TypeError: Chaining cycle detected for promise #<Promise>
解决
- 在
then
方法中返回的Promise
对象就是promise 2 - 那么成功的回调 返回的
Promise
对象就是 x - 判断
peomise2
与x
是否相等, - 相等就是自己返回了自己, 需要将状态放到
reject
改造代码
// 将then方法里面改造成异步代码加入setTimeout()
setTimeout(() => {
// 执行成功调用成功回调函数,拿到返回值
let x = successCallback(this.value);
resolvePromise(promise2, x, resolve, reject)
}, 0)
// 改造resolvePromise方法
function resolvePromise (promise2, x, resolve, reject) {
// 判断是否相等
if (promise2 === x) {
return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
}
if (x instanceof MyPromise) {
// promise 对象
x.then(resolve, reject)
} else {
// 普通值
resolve(x)
}
}
测试一下
let promise = new MyPromise((resolve, reject) => {
resolve('大白菜')
})
let p1 = promise.then((value) => {
console.log(value)
return p1
})
p1.then((value) => {
console.log(value)
}, (err) => {
console.log(err)
})
// 大白菜
// Chaining cycle detected for promise #<Promise>
捕获错误及then链式调用其他状态
在执行构造器中加入try catch
constructor (executor) {
try {
executor(this.resolve, this.reject)
} catch (e) {
this.reject(e);
}
}
// 测试一下
let promise = new myPromise((resolve, reject) => {
throw new Error('executor error')
})
promise.then(value => {
console.log(value)
}, reason => {
console.log(reason.message)
})
// 成功捕获错误 excutor error
then 回调函数捕获错误
// 在then方法的setTimeout 中添加try catch
setTimeout(() => {
try {
let x = successCallback(this.value);
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
}, 0)
// 测试
let promise = new MyPromise((resolve, reject) => {
resolve('大白菜')
})
promise.then((value) => {
console.log(value)
throw new Error('then error')
}, (err) => {
console.log(err.message)
}).then((value) => {
console.log(value)
}, reason => {
console.log('~~~')
console.log(reason.message)
})
// 大白菜
// ~~~
// then error
修改失败的地方
setTimeout(() => {
try {
let x = failCallback(this.reason);
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
}, 0)
// 测试一下
let promise = new MyPromise((resolve, reject) => {
reject('失败')
})
promise.then(value => {
console.log(value)
}, reason => {
console.log(reason)
return '大白菜~~';
}).then(value => {
console.log(value)
})
// 失败
// 大白菜~~
当是异步的时候
将原来的代码改成
this.successCallback.push(successCallback);
this.failCallback.push(failCallback);
this.successCallback.push(() => {
setTimeout(() => {
try {
let x = successCallback(this.value);
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
}, 0)
})
this.failCallback.push(() => {
setTimeout(() => {
try {
let x = failCallback(this.reason);
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
}, 0)
})
// 然后resolve和reject就不需要传值了
while (this.successCallback.length) this.successCallback.shift()();
while (this.failCallback.length) this.failCallback.shift()();
测试一下
let promise = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve('success~~~~');
// reject('error~~~~')
}, 2000)
})
promise.then((value) => {
console.log(value)
return '米糕';
}, reason => {
console.log(reason)
return '大白菜';
}).then((value) => {
console.log(value);
})
// suceess~~~
// 米糕
到这里Promise的核心功能就基本已经实现啦~~~
将then方法的参数变成可选参数
- then方法有两个可选参数,一个成功回调,一个失败回调
- 这两个参数都是可选参数,加入遇到一下情况
let promise = new Promise((resolve, reject) => {
resolve(100)
})
promise
.then()
.then()
.then(value => {
cosole.log(value)
})
- 在这种情况下, promise 会依次往下传递
- 我们需要在then方法中判断是否有参数,如果不存在就补一个参数,这样状态就可以依次向后传递了
修改Promise的代码
在then方法我们判断successCallback和failCallback是否存在
successCallback = successCallback ? successCallback : value => value;
failCallback = failCallback ? failCallback : reason => { throw reason };
测试一下
let promise = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve('success');
// reject('reject');
}, 2000)
})
promise.then().then().then(value => {
console.log(value);
}, reason => {
console.log(reason);
})
// success
// reject
Promise.all实现
- 接收一个数组作为参数填充,在数组中可以填入任意值,包括普通值和
Promise
对象,数组的顺序一定是得到结果的顺序 -
Promise.all
特点在all
中的所有Promise
对象, 如果他的状态都是成功的,那么all
方法就是成功的,如果有一个是失败的, 那么就是失败的 - 利用类 .上
all
所以all
是一个静态方法 -
Promise.all
是解决异步并发问题, 允许按照异步代码调用的顺序得到异步代码执行的结果, 由于all
方法是静态方法,all
前面定义static
关键字, all 方法接收一个数组作为参数,all
方法的返回值也是一个Promise
对象, 在Promise
对象中通过循环 传递的数组,在循环的过程判断是普通值,还是Promise 对象, 进行不同的调用 - 如果参数中有一个
Promise
失败,那么Promise.all
返回的Proise
对象失败
static all(array) {
let result = []
let index = 0
return new MyPromise((resolve, reject) => {
// 执行for 循环有异步操作,循环没有等待异步操作。
// 如果index 等于 array的length 就调用resolve
function addData (key, value) {
result[key] = value;
index++
if (index === array.length) {
resolve(result)
}
}
// 需要判断是普通值, 还是Promise 对象
for (let i = 0; i < array.length; i++) {
let current = array[i];
if (current instanceof MyPromise) {
// promise 对象
current.then(value => addData(i, value), reason => reject(reason) )
} else {
// 普通值
addData(i, array[i])
}
}
})
}
测试一下
function p1() {
return new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve('p1')
}, 1000)
})
}
function p2() {
return new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve('p1')
}, 1000)
})
}
MyPromise.all(['a', 'b', p1(), p2(), 'c']).then(result => {
console.log(result)
})
// [ 'a', 'b', 'p1', 'p1', 'c' ]
Promise.resolve 方法实现
-
Promise.resolve
的作用是将给定的值转换为Promise
对象, 也就是说Promise.resolve
的返回值就就是一个Promise对象,在返回的Promise
对象中会包裹给定的这个值 - 在
resolve
的内部, 会创建一个Promise
对象,并把这个值包裹在Promise
对象中,然后把创建出来的Promise
对象最作为resolve
的返回值,正是因为这样,我们才能后面进行链式调用then
方法, 通过then方法的成功回调函数来拿到这个值,Promise.resolve
也可以接收一个Promise
对象, 在Promise.resolve
内部会判断给定的值是普通值还是Promise
对象,如果是Promise
对象的话,会原封不动把Promise
在作为Promise.resolve
的返回值,所以才能在后在调用then
方法,通过then方法成功回调函数来拿到Promise
对象的返回值
static resolve(value) {
if (value instanceof myPromise) return value;
return new MyPromise(resolve => resolve(value))
}
测试一下
function p1() {
return new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve('p1')
}, 1000)
})
}
MyPromise.resolve('大白菜').then(value => console.log(value))
MyPromise.resolve(p1()).then(value => console.log(value))
// 大白菜
// p1
Promise.reject()
static reject(reason) {
return new MyPromise((resolve,reject) => reject(reason))
}
Promise.finally方法实现
Promise.finally有两个特点
- 无论当前这个
Promise
对象最终的状态是成功还是失败,finally
方法这个会回调函数始终都会执行一次 - 在
finally
的后面可以链式调用then
方法来拿到当前这个Promise
对象最终返回的结果
finally(callback) {
return this.then(value => {
callback();
return value
}, reason => {
callback();
throw reason
})
}
测试一下
function p1() {
return new MyPromise((resolve, reject) => {
resolve('p1 reject')
// reject('p1 reject')
})
}
function p2() {
return new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve('p2')
}, 2000)
})
}
p1().finally(() => {
console.log('finally');
// return p2();
}).then(value => {
console.log(value);
}, reason => {
console.log(reason);
})
// finally
// p1 reject
- 在
finally
的回调函数中,其实可以在return
一个Promise
对象 - return p2后面的then需要等待setTimeout之后执行
- 借助
resolve
方法 - 如果
callback
返回的是普通值,转换Promise
对象, 等待Promise
对象执行完成,如果返回的是Promise
对象,还等待你执行完成,在返回value
优化上面的代码
finally(callback) {
return this.then(value => {
return MyPromise.resolve(callback()).then(() => value);
}, reason => {
return MyPromise.resolve(callback()).then(() => { throw reason },);
})
}
测试一下
function p1() {
return new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve('p1 resolve')
}, 2000)
})
}
function p2() {
return new MyPromise((resolve, reject) => {
resolve('p2 resolve')
// reject('p2 reject')
})
}
p2().finally(() => {
console.log('finally');
return p1()
}).then(value => {
console.log(value);
}, reason => {
console.log(reason);
})
// finally
// 等待2s后执行 输出p2 resolve
Promise.catch 方法实现
-
catch
方法的作用是用来处理当前这个Promise
对象最终状态为失败的情况的,就是说当我们调用then方法时候,我们可以不传递失败回调, 如果不传失败回调,那么失败回调就可以被catch捕获,从而去执行传入到catch方法的回调函数 - 只需要在catch方法内部去调用then方法就可以了
catch (failCallback) {
return this.then(undefind, failCallback);
}
测试一下
function p1() {
return new MyPromise((resolve, reject) => {
// resolve('大白菜~~')
reject('error')
})
}
p1()
.then(value => console.log(value))
.catch(reason => console.log(reason))
Promise.race()
-
Promise.race
只返回第一个执行完毕的Promise
的结果,无论结果是fullfilled
还是rejected
static race(promises) {
return new MyPromise((resolve, reject) => {
if (promises.length === 0) {
return
} else {
for (let p of promises) {
MyPromise.resolve(p).then(value => {
resolve(value)
}, reason => {
reject(reason)
})
}
}
})
}
测试一下
const MyPromise = require('./myPromise');
let promise1 = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve('大白菜')
}, 2000)
});
let promise2 = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve('米糕')
}, 1000)
});
MyPromise.race([promise1, promise2]).then((value) => {
console.log(value);
});
// 米糕
完整代码
const PENDING = 'pending'; // 等待
const FULFILLED = 'fulfilled'; // 成功
const REJECTED = 'rejected'; // 失败
class MyPromise {
constructor (executor) {
try {
executor(this.resolve, this.reject)
} catch (e) {
this.reject(e);
}
}
// promise 状态
status = PENDING;
// 成功后的值
value = undefined;
// 失败后的值
reason = undefined;
// 成功回调
successCallback = [];
// 失败后的回调
failCallback = [];
resolve = value => {
// 如果状态不是等待,阻止程序向下运行
if (this.status !== PENDING) return;
// 将状态更改为成功
this.status = FULFILLED;
// 保存成功之后的值
this.value = value;
// 判断成功回调是否存在,如果存在 调用
// this.successCallback && this.successCallback(this.value);
while (this.successCallback.length) this.successCallback.shift()();
}
reject = reason => {
// 如果状态不是等待,阻止程序向下运行
if (this.status !== PENDING) return;
// 将状态更改为失败
this.status = REJECTED;
// 保存失败后的原因
this.reason = reason;
// 判断失败回调是否存在,如果存在则调用
// this.faliCallback && this.faliCallback(this.reason);
while (this.failCallback.length) this.failCallback.shift()();
}
then (successCallback, failCallback) {
// 根据规范,如果then的参数不是function,则我们需要忽略它, 让链式调用继续往下执行
successCallback = typeof successCallback === 'function' ? successCallback : value => value;
failCallback = typeof failCallback === 'function' ? failCallback : reason => { throw reason };
let promise2 = new MyPromise((resolve, reject) => {
// 判断状态
if (this.status === FULFILLED) {
setTimeout(() => {
try {
let x = successCallback(this.value);
// 判断x的是是普通值还是promise对象
// 如果是普通值, 直接调用resolve
// 如是是promise对象 查看promise对象返回的结果
// 在根据promise对象返回的结果 决定调用resovle还是reject
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
}, 0)
} else if (this.status === REJECTED) {
setTimeout(() => {
try {
let x = failCallback(this.reason);
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
}, 0)
} else {
// 等待
// 将等待回调和失败回调存储起来
this.successCallback.push(() => {
setTimeout(() => {
try {
let x = successCallback(this.value);
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
}, 0)
}) // 将成功回调推入
this.failCallback.push(() => {
setTimeout(() => {
try {
let x = failCallback(this.reason);
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
}, 0)
}) // 将失败回调推入
}
});
return promise2;
}
static all(array) {
let result = []
let index = 0
return new MyPromise((resolve, reject) => {
// 执行for 循环有异步操作,循环没有等待异步操作。
// 如果index 等于 array的length 就调用resolve
function addData (key, value) {
result[key] = value;
index++;
// 保证all的每一项都执行完了
if (index === array.length) {
resolve(result);
}
}
// 需要判断是普通值, 还是Promise 对象
for (let i = 0; i < array.length; i++) {
let current = array[i];
if (current instanceof MyPromise) {
// promise 对象
current.then(value => addData(i, value), reason => reject(reason));
} else {
// 普通值
addData(i, array[i])
}
}
})
}
static resolve(value) {
if (value instanceof MyPromise) return value;
return new MyPromise(resolve => resolve(value))
}
static reject(reason) {
return new MyPromise((resolve,reject) => reject(reason))
}
static race(promises) {
return new MyPromise((resolve, reject) => {
if (promises.length === 0) {
return
} else {
for (let p of promises) {
MyPromise.resolve(p).then(value => {
resolve(value)
}, reason => {
reject(reason)
})
}
}
})
}
// finally链式调用返回Promise
finally(callback) {
return this.then(value => {
return MyPromise.resolve(callback()).then(() => value);
}, reason => {
return MyPromise.resolve(callback()).then(() => { throw reason },);
})
}
catch (failCallback) {
return this.then(undefined, failCallback);
}
}
function resolvePromise (promise2, x, resolve, reject) {
// 判断是否相等
if (promise2 === x) {
return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
}
if (x instanceof MyPromise) {
// promise 对象
x.then(resolve, reject)
} else {
// 普通值
resolve(x)
}
}
module.exports = MyPromise;
总结
以上就是对Promise的实现的整个过程,我们首先从一个简单Promise的使用实例开始,对Promise的核心进行了分析,根据分析我们实现了大致的一个结构,然后根据Promise A+规范一步一步的进行填充代码。主要实现了
- Promise 异步逻辑
- 实现then 方法多次调用添加多个处理函数
- Promise.then()方法链式调用
- Promise.then()参数为可选参数
- Promise.all()
- Promise.resolve()
- Peomise.reject()
- Promise.finally()
- Promise.catch()
- Promise.rece()
感谢大家
最后感谢您花宝贵的时间阅读这篇内容,如果你觉得这篇内容对你有帮助的话,就给本文点个赞吧,
(感谢大家的鼓励与支持🌹🌹🌹)
参考
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。