引言
上一篇介绍了Promise的简单实现,理解了promise解决异步问题的原理,今天来完善下promise的方法 上一篇文章结束用ES6语法实现promise基本功能
class Promise1 {
// 实例属性和方法
constructor(executor) {
let that = this;
this.state = 'pending';
this.value = undefined;
this.reason = undefined;
this.onResolvedCallbacks = []; // 异步成功回调
this.onRejectedCallbacks = []; // 异步失败回调
try{
executor(resolve, reject)
}catch(err){
console.log('执行器捕获异常',err)
}
function resolve(val) {
if(that.state === 'pending') {
that.value = val;
that.onResolvedCallbacks.forEach(cb => {
cb(val);
})
that.state = 'resolved';
}
}
function reject(err) {
if(that.state === 'pending') {
that.reason = err;
that.onRejectedCallbacks.forEach(cb => {
cb(err);
})
that.state = 'rejected';
}
}
}
// 原型方法
then(onFulfilled, onRejected) {
if(this.state === 'pending') {
if(typeof(onFulfilled) === 'function'){
this.onResolvedCallbacks.push(onFulfilled);
}
if(typeof(onRejected) === 'function'){
this.onRejectedCallbacks.push(onRejected);
}
}
if(this.state === 'resolved' && typeof(onFulfilled) === 'function') {
onFulfilled(this.value);
}
if(this.state === 'rejected' && typeof(onRejected) === 'function') {
onFulfilled(this.value);
}
}
}
let p = new Promise1((resolve, reject)=>{
setTimeout(() => {
resolve('前端大菜鸟');
}, 10);
})
p.then(res=> {
console.log(res); // 前端大菜鸟
},err=>{
console.log(err);
})
then链式调用
Promise之所以能够进行链式调用,是因为then()方法内部返回了一个Promise实例(promise2),这个Promise实例(promise2)才可以继续调用then()方法。并且这个Promise实例(promise2) resolve函数的参数,是上一个then的resolve函数的结果。reject同理。
效果
先看下效果
new Promise((resolve, reject) => {
resolve(123)
}).then((res) => {
console.log(res)
return 456
}).then((res) => {
console.log(res)
return 789
}).then((res) => {
console.log(res)
})
// 打印输出 123 456 789
改造第一步
根据上面的功能来改造then方法,返回一个新的promise实例(promise2)
class Promise1{
...
then((onFulfilled, onRejected) {
// ********定义一个Promise2**************
let Promise2 = new Promise1((resolve, reject) => {
if(this.state === 'pending') {
this.onResolvedCallbacks.push(() => {
let res = onFulfilled(this.value);
resolve(res); // *promise2 resolve的参数是上一个promise then的resolve(onFulfilled)的结果
});
this.onRejectedCallbacks.push(() => {
let res = onRejected(this.reason) //
reject(res); // *promise2 reject的参数是上一个promise then的reject(onRejected)的结果
});
}
if(this.state === 'resolved') {
let res = onFulfilled(this.value); //
resolve(res); // *promise2 resolve的参数是上一个promise then的resolve(onFulfilled)的结果
}
if(this.state === 'rejected') {
let res = onRejected(this.reason); //
reject(res); // *promise2 reject的参数是上一个promise then的reject(onRejected)的结果
}
})
// 返回Promise2
return Promise2;
}
}
let p = new Promise1((resolve, reject)=>{
setTimeout(() => {
resolve('前端大菜鸟');
}, 10);
})
p.then(res=> {
console.log(res); // 前端大菜鸟
return 123
}).then(res1=>{
console.log(res1); // 123
})
// 前端大菜鸟 123
可以看到,then方法返回了一个新的Promise实例(promise2),并且将then(onFullFilled,onRejected)的回调的结果作为新的Promise实例(promise2)的参数resolve或者reject出去了。至此实现了普通值得链式调用。对于then返回的结果(onFullFilled、onRejected) 还有以下这几种情况。
- 返回一个函数,新的Promise实例(promise2)的then的onFullFilled函数会直接以这个函数当作参数
// ...
let p = new Promise1((resolve, reject)=>{
setTimeout(() => {
resolve('前端大菜鸟');
}, 10);
})
p.then(res=> {
console.log(res);
return ()=>{console.log('小刺鸟');} // 返回一个函数
}).then(res1=>{ // 可以看到此时then的参数就是上个promise返回的函数
console.log(res1()); // 小刺鸟
})
- 返回一个promise,新的Promise实例(promise2)的状态取决于上个返回的promise的状态,新的Promise实例(promise2)的返回值 是上个返回的promise的resolve() / reject()值。
// ...
let p = new Promise1((resolve, reject)=>{
setTimeout(() => {
resolve('前端大菜鸟');
}, 10);
})
p.then(res=> {
console.log(res); // 前端大菜鸟
// 这里返回一个promise
return new Promise1((resolve, reject)=> {
resolve('我是小菜鸟1号')
})
}).then(res1=>{
console.log(res1); // 我是小菜鸟1号
})
// reject
p.then(res=> {
console.log(res); // 前端大菜鸟
// 这里返回一个promise
return new Promise1((resolve, reject)=> {
reject('我是小菜鸟2号')
})
}).then(res1=>{
console.log(res1); // 接受不到
},err=> {
console.log(err) // 我是小菜鸟2号
})
// 可以看出当then返回一个promise时 新的promise实例(promise2)的状态是取决于上个返回的promise的状态 , 返回值也是上个promise resolve/reject的值
改造第二步
根据上述情况,此时,我们需要一个函数(formatPromise)来判断上一个then的onFullFilled/onrejected的返回值到底是普通值还是函数还是Promise对象。并且,当值不同时采用不一样的处理方式。
// promise2:新的Promise实例(promise2);result:上个promise的resolve() / reject()值
function formatPromise (promise2, result, resolve, reject){
// 首先,先判断下promise2和result是否一样, 因为result是一个返回值,他有可能是一个Promise,那如果他直接返回第一个参数中的promise会造成死循环。
if (promise2 === result) {
return new Error('Promise循环引用')
}
// 判断是否是对象
if (typeof result === 'object' && result != null) {
try {
// 如果是对象,先看是否存在then函数
let then = result.then
// 如果result.then是一个函数,就当成是Promise对象
if (typeof then === 'function') {
// 利用.call将this指向result, 防止result.then()报错
then.call(result, res => {
resolve(res)
}, err => {
reject(err)
})
} else {
// 如果不是function,那么说明只是普通对象,并不是Promise对象,当普通值处理
resolve(result)
}
} catch (err) {
reject(err)
}
} else {
// 不是对象,那就是普通值或者函数,直接resolve()
resolve(result)
}
}
改造第三步
我们构造好formatPromise函数后再去改造一下then函数,已知Promise 中处理器函数是同步执行,而then方法是异步的且是个微任务,所以我们用queueMicrotask来实现微任务。
class Promise1{
// ...
then(onFulfilled, onRejected) {
// ********定义一个Promise2**************
let Promise2 = new Promise1((resolve, reject) => {
onFulfilled = typeof onFulfilled === "function" ? onFulfilled : value => value;
onRejected = typeof onRejected === "function" ? onRejected : (err) => { throw err };
if(this.state === 'pending') {
this.onResolvedCallbacks.push(() => {
queueMicrotask(() => {
let res = onFulfilled(this.value);
// 调用formatPromise函数
formatPromise(Promise2, res, resolve, reject); // *promise2 resolve的参数是上一个promise then的resolve(onFulfilled)的结果
})
});
this.onRejectedCallbacks.push(() => {
queueMicrotask(() => {
let res = onRejected(this.reason)
// 调用formatPromise函数
formatPromise(Promise2, res, resolve, reject); // *promise2 reject的参数是上一个promise then的reject(onRejected)的结果
})
});
}
if(this.state === 'resolved') {
queueMicrotask(() => {
let res = onFulfilled(this.value);
// 调用formatPromise函数
formatPromise(Promise2, res, resolve, reject); // *promise2 resolve的参数是上一个promise then的resolve(onFulfilled)的结果
})
}
if(this.state === 'rejected') {
queueMicrotask(() => {
let res = onRejected(this.reason);
// 调用formatPromise函数
formatPromise(Promise2, res, resolve, reject); // *promise2 reject的参数是上一个promise then的reject(onRejected)的结果
})
}
})
// 返回Promise2
return Promise2;
}
}
至此我们完成了promise的链式调用功能,当然现在有很多不足的地方,比如捕获异常和promise同时调用只能调用一次等等,感兴趣的同僚可以自行去了解一下吧
catch()
其实.catch(),和.then()的reject回调是一样的。只是使用位置不一样罢了。并且.catch()也支持链式调用。也就是说.catch和.then一样,也返回了一个Promise对象,所以其实catch内部就是执行了一个只有reject回调的then函数。
Promise1.prototype.catch = function (onRejected) {
return this.then(null, onRejected)
}
resolve()
将现有对象转为 Promise 对象
const isPromise = (value) => {
if ((value != null && typeof value === 'object') || typeof value === 'function') {
if (typeof value.then == 'function') {
return true
}
} else {
return false
}
}
Promise1.resolve = (value) => {
// 如果是promise对象就直接将这个对象返回
if (isPromise(value)) {
return value
} else {
// 如果是一个普通值就将这个值包装成一个promise对象之后返回
return new Promise1((resolve, reject) => {
resolve(value)
})
}
}
reject()
返回一个新的 Promise 实例,该实例的状态为rejected
Promise1.reject = (value) => {
return new Promise1((resolve, reject) => {
reject(value)
})
}
all()
Promise1.all = (array) => {
//接收数组
if (!Array.isArray(array)) {
return reject(new TypeError('must array'))
}
let result = [] // 处理结果的数组
let index = 0
return new Promise1((resolve, reject) => {
for (let i = 0; i < array.length; i++) {
let current = array[i]
if (current instanceof Promise1) {
current.then(
(value) => {
addData(i, value)
},
(reason) => reject(reason)
)
} else {
//普通值
addData(i, array[i])
}
}
//返回一个promise对象
function addData(key, value) {
result[key] = value
index++
if (index === array.length) resolve(result)
}
})
}
}
完整代码(仅then)
class Promise1 {
constructor(executor) {
let that = this;
this.state = 'pending';
this.value = undefined;
this.reason = undefined;
this.onResolvedCallbacks = []; // 异步成功回调
this.onRejectedCallbacks = []; // 异步失败回调
executor(resolve, reject)
function resolve(val) {
if(that.state === 'pending') {
that.value = val;
that.onResolvedCallbacks.forEach(cb => {
cb();
})
that.state = 'resolved';
}
}
function reject(err) {
if(that.state === 'pending') {
that.reason = err;
that.onRejectedCallbacks.forEach(cb => {
cb();
})
that.state = 'rejected';
}
}
}
then(onFulfilled, onRejected) {
// ********定义一个Promise2**************
let Promise2 = new Promise1((resolve, reject) => {
onFulfilled = typeof onFulfilled === "function" ? onFulfilled : value => value;
onRejected = typeof onRejected === "function" ? onRejected : (err) => { throw err };
if(this.state === 'pending') {
this.onResolvedCallbacks.push(() => {
queueMicrotask(() => {
let res = onFulfilled(this.value);
formatPromise(Promise2, res, resolve, reject); // *promise2 resolve的参数是上一个promise then的resolve(onFulfilled)的结果
})
});
this.onRejectedCallbacks.push(() => {
queueMicrotask(() => {
let res = onRejected(this.reason) //
formatPromise(Promise2, res, resolve, reject); // *promise2 reject的参数是上一个promise then的reject(onRejected)的结果
})
});
}
if(this.state === 'resolved') {
queueMicrotask(() => {
let res = onFulfilled(this.value); //
formatPromise(Promise2, res, resolve, reject); // *promise2 resolve的参数是上一个promise then的resolve(onFulfilled)的结果
})
}
if(this.state === 'rejected') {
queueMicrotask(() => {
let res = onRejected(this.reason); //
formatPromise(Promise2, res, resolve, reject); // *promise2 reject的参数是上一个promise then的reject(onRejected)的结果
})
}
})
// 返回Promise2
return Promise2;
}
}
function formatPromise (promise2, result, resolve, reject){
// 首先,先判断下promise2和result是否一样, 因为result是一个返回值,他有可能是一个Promise,那如果他直接返回第一个参数中的promise是会造成死循环的。
if (promise2 === result) {
return new Error('Promise循环引用')
}
// 判断是否是对象
if (typeof result === 'object' && result != null) {
try {
// 如果是对象,先看是否存在then函数
let then = result.then
// 如果result.then是一个函数,就当成是Promise对象
if (typeof then === 'function') {
// 利用.call将this指向result, 防止result.then()报错
then.call(result, res => {
resolve(res)
}, err => {
reject(err)
})
} else {
// 如果不是function,那么说明只是普通对象,并不是Promise对象,当普通值处理
resolve(result)
}
} catch (err) {
reject(err)
}
} else {
// 不是对象,那就是普通值或者函数,直接resolve()
resolve(result)
}
}
let p = new Promise1((resolve, reject)=>{
setTimeout(() => {
resolve('我是前端路上的一个小菜鸟');
}, 10);
})
p.then(res=> {
console.log(res);
return new Promise1((resolve)=>{
resolve('我在不断进步!!!')
})
}).then(res1=>{
console.log(res1);
})
// 输出 我是前端路上的一个小菜鸟 我在不断进步!!!
结语
promise的介绍就止于此,如果感觉有帮助的大佬们,请留个赞,如果说有写的不对的地方,也请大佬们留言指正。