学习了Promise的A+规范,以及手写一个Promise后,我对Promise学到的结果。
平常也有用到过promise来处理异步,先回顾下promise的用法
new Promise((resolve,reject) => {异步操作并改变状态}).then(onfullfilled, onrejected)
const myPromise = new Promise((resolve,reject) => {
//写一些异步操作,比如ajax请求
if(window.onError){
reject('页面加载失败');
}
setTimeout(() => {
resolve('异步返回结果');
}, 1000)
}).then((data) => {
console.log(data); // 异步返回结果
}).catch((reason)=>{
console.log(reason); // 页面加载失败
})
这个promise其实就是处理一件一段时间之后才能得到结果的事件,promise将将要执行的事件作为函数传入,并在得到结果时,resolve或者reject改变promise的状态并且改变结果,然后通过then或catch执行得到结果之后的事情。
pending 初始化,可改变
fullfilled 成功,不可改变
rejected 失败,不可改变
resolve:实参是定义在构造函数中的函数,
作用:
改变状态pending => fullfilled
改变结果 value = data
reject:实参是定义在构造函数中的函数,
作用:
改变状态pending => rejected,
改变结果 reason= reason
promise.then(onFulfilled, onRejected)
1.onFulfilled和onRejected,必须传入函数,如果不是函数,则改变为直接返回value或者reason的函数。
2.onFulfilled和onRejected只有当状态改变为相应的值后才可以执行,并将value和reason作为参数,且只可以执行一次
3.onFulfilled 和 onRejected 应该是微任务,这里用queueMicrotask来实现微任务的调用
4.then方法可以被调用多次
4.1promise状态变成 fulfilled 后,所有的 onFulfilled 回调都需要按照then的顺序执行, 也就是按照注册顺序执行(所以在实现的时候需要一个数组来存放多个onFulfilled的回调)
4.2promise状态变成 rejected 后,所有的 onRejected 回调都需要按照then的顺序执行, 也就是按照注册顺序执行(所以在实现的时候需要一个数组来存放多个onRejected的回调)
5.then 应该返回一个promise
promise2 = promise1.then(onFulfilled, onRejected);
5.1 onFulfilled 或 onRejected 执行的结果为x, 调用 resolvePromise
5.2 如果 onFulfilled 或者 onRejected 执行时抛出异常e, promise2需要被reject
5.3 如果 onFulfilled 不是一个函数, promise2 以promise1的value 触发fulfilled
5.4 如果 onRejected 不是一个函数, promise2 以promise1的reason 触发rejected
不管是new Promise((resolve,reject)=> {})还是then((data) => {}),传入的函数都是在构造函数内执行的,所以实参是在构造函数内定义赋值的,所以我们再手写一个promise时,需要定义resolve和reject函数,以及得到的value,作为函数的实参
代码主要实现了A+规范里的内容:
如果 promise2 和 x 相等,那么 reject TypeError
如果 x 是一个 promsie
如果x是pending态,那么promise必须要在pending,直到 x 变成 fulfilled or rejected.
如果 x 被 fulfilled, fulfill promise with the same value.
如果 x 被 rejected, reject promise with the same reason.
如果 x 是一个 object 或者 是一个 function
let then = x.then.
如果 x.then 这步出错,那么 reject promise with e as the reason.
如果 then 是一个函数,then.call(x, resolvePromiseFn, rejectPromise)
resolvePromiseFn 的 入参是 y, 执行 resolvePromise(promise2, y, resolve, reject);
rejectPromise 的 入参是 r, reject promise with r.
如果 resolvePromise 和 rejectPromise 都调用了,那么第一个调用优先,后面的调用忽略。
如果调用then抛出异常e
如果 resolvePromise 或 rejectPromise 已经被调用,那么忽略
则,reject promise with e as the reason
new MyPromise((resolve,reject) => {}).then(() => {}, () => {})
new MyPromise((resolve,reject) => {}).then(() => {}).catch(() => {})
从用法可以看出我们需要做的功能有
1.实现一个构造函数
2.构造函数接收一个函数作为参数
3.我们要再构造函数里面执行传进来的参数,并且传递实参resolve,reject, 作为提供给用户更改promise状态的方法
4.实现resolve,reject
5.实现then:
接收两个函数参数,分别在状态改变成对应值时触发
为了实现链式操作,继续让then返回一个promise
6.从promiseA+规范得知,我们还要实现一个resolvePromise去处理then返回的数据
class MyPromise {
construcor() {}
}
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
class MyPromise {
construcor() {
this.status = PENDING; // 初始状态为pending
this.value = null;
this.reason = null;
}
}
class MPromise {
constructor() {
// 初始状态为pending
this.status = PENDING;
this.value = null;
this.reason = null;
}
resolve(value) {
if (this.status === PENDING) {
this.value = value;
this.status = FULFILLED;
}
}
reject(reason) {
if (this.status === PENDING) {
this.reason = reason;
this.status = REJECTED;
}
}
}
class MPromise {
constructor(fn) {
// 初始状态为pending
this.status = PENDING;
this.value = null;
this.reason = null;
// 对用户自己传进来执行的代码块一般都做下try-catch
try {
// 这里绑定this是因为resolve执行的时候this的指向不一定是这个promise了,而我们要让他指向这里才能使用this.status...
fn(this.resolve.bind(this),this.reject.bind(this))
} catch(e) {
this.reject(e)
}
}
resolve(value) {
if (this.status === PENDING) {
this.value = value;
this.status = FULFILLED;
}
}
reject(reason) {
if (this.status === PENDING) {
this.reason = reason;
this.status = REJECTED;
}
}
}
a.接收两个函数参数,分别在状态改变成对应值时触发
为了实现链式操作,继续让then返回一个promise
then(onFulfilled, onRejected) {
const realOnFulfilled = this.isFunction(onFulfilled)
? onFulfilled
: (value) => {
return value;
};
const realOnRejected = this.isFunction(onRejected)
? onRejected
: (value) => {
return value;
};
const promise2 = new MPromise((resolve, reject) => {
switch (this.status) {
case FULFILLED: {
realOnFulfilled(value);
}
case REJECTED: {
realOnRejected(value);
}
}
});
return promise2;
}
isFunction(value) {
return typeof value === "function";
}
b.考虑到如果传入的函数中执行异步之后resolve,而执行then中的函数时,还状态还是pending
// 定义两个数组存放还没有执行的回调函数
FULLFILLED_CALLBACK_LIST = [];
REJECTED_CALLBACK_LIST = [];
// 做一下status的监听
_status = PENDING;
get status() {
return _status;
}
set status(value) {
this._status = value;
if (value === FULFILLED) {
this.FULLFILLED_CALLBACK_LIST.forEach((callback) => callback(this.value));
} else if (value === REJECTED) {
this.REJECTED_CALLBACK_LIST.forEach((callback) => callback(this.reason));
}
}
then(onFulfilled, onRejected) {
const realOnFulfilled = this.isFunction(onFulfilled)
? onFulfilled
: (value) => {
return value;
};
const realOnRejected = this.isFunction(onRejected)
? onRejected
: (value) => {
return value;
};
const promise2 = new MPromise((resolve, reject) => {
switch (this.status) {
case FULFILLED: {
try {
realOnFulfilled(value);
} catch (error) {
reject(error);
}
}
case REJECTED: {
try {
realOnRejected(value);
} catch (error) {
reject(error);
}
}
case PENDING: {
this.FULLFILLED_CALLBACK_LIST.push(realOnFulfilled);
this.REJECTED_CALLBACK_LIST.push(realOnRejected);
}
}
});
return promise2;
}
isFunction(value) {
return typeof value === "function";
}
resolvePromise(promise2, x, resolve, reject) {
if (promise2 === x) {
reject(new TypeError("the promise and the return value are the same"));
}
if (x instanceof MPromise) {
x.then((y) => {
resolvePromise(promise2, y, resolve, reject);
});
} else if (typeof x === "object" || this.isFunction(x)) {
if ((x = null)) {
return resolve(x);
}
let then = null;
try {
then = x.then;
} catch (error) {
return reject(error);
}
if (this.isFunction(then)) {
const called = false;
try {
then(
x,
(y) => {
if(called) return;
called = true;
resolvePromise(promise2, y, resolve, reject);
},
(r) => {
if(called) return;
called = true;
reject(r);
}
);
} catch (error) {
if (called) return;
reject(error)
}
} else {
resolve(x);
}
} else {
resolve(x);
}
}
使用queueMicrotask
catch其实就是调用then方法,传入一个回调函数
catch(onRejected){
return this.then(null, onRejected);
}
这里注意resolve传入的参数有可能是promise也有可能是一个值
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));
}
// finally原则上也是then方法实现,注意传入的回调中不用传参,再次点then才能获取到value或者err
static finally(callback) {
return this.then(
(value) => {
return MyPromise.resolve(callback()).then(() => value); // 无论状态变为fullfilled还是rejected都执行回调
},
(err) => {
return MyPromise.resolve(callback()).then(() => {throw err;}); // 无论状态变为fullfilled还是rejected都执行回调
})
}
注意all和race都是异步过程中返回的;
race(arr) {
const len = arr.length;
return new MyPromise((resolve,reject) => {
if(len === 0) {
resolve();
}
for(let i = 0; i < len; i++){
MyPromise.resolve(arr[i]).then(
(value) => {
resolve(value);
},
(reason) => {
reject(reason)
}
)
}
})
}
all(arr) {
const len = arr.length;
return new MyPromise((resolve,reject) => {
let res = [];
let count = 0;
if(len === 0) {
resolve();
}
for(let i = 0; i < len; i++){
MyPromise.resolve(arr[i]).then(
(value) => {
res[i] = value;
count++;
if(count === len) {
resolve(res);
}
},
(reason) => {
reject(reason)
}
)
}
})
}
const test1 = new MyPromise((resolve) => {
setTimeout(() => {
resolve(123);
}, 2000);
}).then(console.log);
console.log(test1); //pending value: null
setTimeout(() => {
console.log(test1); // fullfilled value: undefined then函数没有返回值则返回值为undefined即x=undefined,则promise在解析时则resolve(x)
}, 3000);
const test = new MPromise((resolve, reject) => {
setTimeout(() => {
resolve(111);
}, 1000);
})
.then((value) => {})
.then(() => {})
.then(() => {})
.then(() => {})
.then(() => {});
/* 这个问题提出的时候, 应该是有一个假定条件, 就是链式调用的时候.
这个时候, 每一个.then返回的都是一个新promise, 所以每次回调数组FULFILLED_CALLBACK_LIST都是空数组.
针对这种情况, 确实用数组来存储回调没意义, 完全可以就用一个变量来存储。 */
// 但是还有一种promise使用的方式, 这种情况下, promise实例是同一个, 数组的存在就有了意义
const test = new MPromise((resolve, reject) => {
setTimeout(() => {
resolve(111);
}, 1000);
})
test.then(() => {});
test.then(() => {});
test.then(() => {});
test.then(() => {});
const test = new MPromise((resolve, reject) => {
setTimeout(() => {
reject(111);
}, 1000);
}).catch((reason) => {
console.log('报错' + reason);
console.log(test) // pending
});
setTimeout(() => {
console.log(test); // fullfilled
}, 3000)
完整代码
/*
new MyPromise((resolve,reject) => {}).then(() => {}, () => {})
new MyPromise((resolve,reject) => {}).then(() => {}).catch(() => {})
*/
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";
class MyPromise {
FULLFILLED_CALLBACK_LIST = [];
REJECTED_CALLBACK_LIST = [];
_status = PENDING;
constructor(fn) {
this.status = PENDING;
this.value = null;
this.reason = null;
try {
fn(this.resolve.bind(this), this.reject.bind(this));
} catch (error) {
this.reject(error);
}
}
get status() {
return this._status;
}
set status(value) {
this._status = value;
if (value === FULFILLED) {
this.FULLFILLED_CALLBACK_LIST.forEach((callback) => callback(this.value));
} else if (value === REJECTED) {
this.REJECTED_CALLBACK_LIST.forEach((callback) => callback(this.reason));
}
}
resolve(value) {
if (this.status === PENDING) {
this.status = FULFILLED;
this.value = value;
}
}
reject(reason) {
if (this.status === PENDING) {
this.status = REJECTED;
this.reason = reason;
}
}
then(onFulfilled, onRejected) {
const realOnFulfilled = this.isFunction(onFulfilled)
? onFulfilled
: (value) => {
return value;
};
const realOnRejected = this.isFunction(onRejected)
? onRejected
: (reason) => {
throw reason;
};
const promise2 = new MyPromise((resolve, reject) => {
const fulfilledMicrotask = () => {
queueMicrotask(() => {
try {
const x = realOnFulfilled(this.value);
this.resolvePromise(promise2, x, resolve, reject);
} catch (error) {
reject(error);
}
});
};
const rejectedMicrotask = () => {
queueMicrotask(() => {
try {
const x = realOnRejected(this.reason);
this.resolvePromise(promise2, x, resolve, reject);
} catch (error) {
reject(error);
}
});
};
switch (this.status) {
case FULFILLED: {
fulfilledMicrotask();
break;
}
case REJECTED: {
rejectedMicrotask();
break;
}
case PENDING: {
this.FULLFILLED_CALLBACK_LIST.push(fulfilledMicrotask);
this.REJECTED_CALLBACK_LIST.push(rejectedMicrotask);
}
}
});
return promise2;
}
catch(onRejected) {
return this.then(null, onRejected);
}
resolvePromise(promise2, x, resolve, reject) {
if (promise2 === x) {
return reject(
new TypeError("the promise and the return value are the same")
);
}
if (x instanceof MyPromise) {
queueMicrotask(() => {
x.then((y) => {
resolvePromise(promise2, y, resolve, reject);
}, reject);
});
} else if (typeof x === "object" || this.isFunction(x)) {
if ((x = null)) {
return resolve(x);
}
let then = null;
try {
then = x.then;
} catch (error) {
return reject(error);
}
if (this.isFunction(then)) {
const called = false;
try {
then.call(
x,
(y) => {
if (called) return;
called = true;
resolvePromise(promise2, y, resolve, reject);
},
(r) => {
if (called) return;
called = true;
reject(r);
}
);
} catch (error) {
if (called) return;
reject(error);
}
} else {
resolve(x);
}
} else {
resolve(x);
}
}
isFunction(value) {
return typeof value === "function";
}
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(promiseList) {
const length = promiseList.length;
return new MyPromise((resolve, reject) => {
if (promiseList.length === 0) return resolve();
promiseList.forEach((promise) =>
MyPromise.resolve(promise).then(
(value) => resolve(value),
(reason) => reject(reason)
)
);
});
}
static all(promiseList) {
return new MyPromise((resolve, reject) => {
let res = [];
let count = 0;
if (promiseList.length === 0) return resolve();
promiseList.forEach((promise, index) => {
MyPromise.resolve(promise).then(
(value) => {
res[index] = value;
count++;
if (count === promiseList.length) {
resolve(res);
}
},
(reason) => {
reject(reason);
}
);
});
});
}
finally(callback) {
return this.then(
(value) => {
return MyPromise.resolve(callback()).then(() => value);
},
(err) => {
return MyPromise.resolve(callback()).then(() => {
throw err;
});
}
);
}
}
// all的测试
// let p1 = MyPromise.resolve(123);
// let p2 = MyPromise.resolve(456);
// let p3 = MyPromise.resolve(789);
// const test = MyPromise.all([p1, p2, p3])
// .then((result) => {
// console.log(result, "then");
// })
// .catch((reason) => {
// console.log(reason, "catch");
// });
// 课后练习1
const test1 = new MyPromise((resolve) => {
setTimeout(() => {
resolve(123);
}, 2000);
}).then(console.log);
console.log(test1); //pending value: null
setTimeout(() => {
console.log(test1); // fullfilled value: undefined then函数没有返回值则返回值为undefined即x=undefined,则promise在解析时则resolve(x)
}, 3000);
// 课后练习2: .then返回的是一个新Promise, 那么原来promise实现的时候, 用数组来存回调函数有什么意义?
const test = new MPromise((resolve, reject) => {
setTimeout(() => {
resolve(111);
}, 1000);
})
.then((value) => {})
.then(() => {})
.then(() => {})
.then(() => {})
.then(() => {});
/* 这个问题提出的时候, 应该是有一个假定条件, 就是链式调用的时候.
这个时候, 每一个.then返回的都是一个新promise, 所以每次回调数组FULFILLED_CALLBACK_LIST都是空数组.
针对这种情况, 确实用数组来存储回调没意义, 完全可以就用一个变量来存储。 */
// 但是还有一种promise使用的方式, 这种情况下, promise实例是同一个, 数组的存在就有了意义
const test = new MPromise((resolve, reject) => {
setTimeout(() => {
resolve(111);
}, 1000);
})
test.then(() => {});
test.then(() => {});
test.then(() => {});
test.then(() => {});
// 课后练习3: 只有当onRjected执行报错的时候才会reject第二个promise,否则最终都会执行到resolve(x)
const test = new MPromise((resolve, reject) => {
setTimeout(() => {
reject(111);
}, 1000);
}).catch((reason) => {
console.log('报错' + reason);
console.log(test) // pending
});
setTimeout(() => {
console.log(test); // fullfilled
}, 3000)