先理解Promise做了哪些事情和原理
new Promise说明Promise是个构造函数
① 必填参数:executor是一个函数,不是函数的话,会报错的
② 返回一个promise实例对象
executor 是立即执行的函数,它接收两个形参也是函数:resolve、reject
promise实例的状态和值
① 默认状态:pending,默认值undefined
② 变为成功态:执行resolve函数时,改变状态为:fulfilled,值:函数的参数
③ 变为失败态:
=> 执行reject函数时,改变状态为:rejected,值:函数的参数
=> executor函数执行时,报错了,改变状态为:rejected,值:报错的原因【不影响其他代码执行】
promise实例是说一不二的主,状态只改变一次
每一个promise实例上有一个then方法,接收两个形参也是函数:onFulfilled、onRejected
① then方法是同步执行的,它的参数现在只是注入,等着promise实例有结果了再执行
② onFulfilled 接收成功态的结果
③ onRejected 接收失败的结果
.then(onFulfilled, onRejected)
① 如果此时promise实例已经是成功或者失败,创建一个异步微任务(JS自己写不了微任务,这里咱用定时器代替),等待同步任务结束,执行对应的函数即可
② 此时状态还是pending,我们需要把onFulfilled、onRejected保存起来,当后期状态修改了(比如:resovle/reject方法执行),再次通知保存的方法,而这个操作也是异步微任务
Promise的then用法
1、 then方法创建并返回了一个新的promise实例
2、 then方法的参数:onFulfilled、onRejected 不是函数,会穿透延顺到下一个then方法中去
3、 失败态的promise延顺到最后,会被catch接收到
Promise对象上的方法
(1)Promise.resolve
Promise.resolve = function (value){
return new Promise(function (resolve){
resolve(value);
});
};
(2)Promise.reject
Promise.reject = function (value){
return new Promise(function (_, reject){
reject(value);
});
};
(3)Promise.all
Promise.all的使用:
1、接收一个数组为参数,返回一个promise实例
2、数组中,有一个失败,则结果就是失败了
3、数组所有的内容都成功了,按照原来的顺序排列结果
① 数组里面的内容是promise的话,要拿到它的结果才行
② 数组里面的内容不是promise的话,收集起来
③ 收集到所有的结果以后,就让返回的promise实例成功
Promise.all = function (promises) {
// 返回了一个promise实例对象
return new Promise(function (resolve, reject) {
try {
var index = 0,
len = promises.length,
results = [];
for (var i = 0; i < len; i++) {
(function (i) { // 自执行匿名函数,保证i的正确性
var item = promises[i];
if (!isPromise(item)) { // 不是Promise
index++;
results[i] = item;
index === len ? resolve(results) : null;
return;
}
// 是Promsie调用它的then方法
item.then(function (result) {
index++;
results[i] = result;
index === len ? resolve(results) : null;
}, function (reason) {
reject(reason);
});
})(i);
}
} catch (err) {
reject(err);
}
});
};
然后将上面的内容翻译成代码:
思路:首先想想我们平时使用的 promise,
先定义三个常量表示状态
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
新建 MyPromise 类(这样就直接可以在内部写then()、all()等)
ES5中创建类的方法:新建一个构造函数,定义一个方法并且赋值给构造函数的原型
ES6实现类非常简单,只需要类声明,和ES5中使用构造函数不同的是,在ES6中,我们将原型的实现写在了类中,但本质上还是一样的,都是需要新建一个类名,然后实现构造函数,再实现原型方法。私有属性是实例中的属性,不会出现在原型上,且只能在类的构造函数或方法中创建。
// 新建 MyPromise 类
class MyPromise {
constructor(executor){
// executor 是一个执行器,进入会立即执行
// 传入resolve和reject方法
try {
executor(this.resolve, this.reject)
} catch (error) {
this.reject(error)
}
}
// 储存状态的变量,初始值是 pending
status = PENDING;
// 成功之后的值
value = null;
// 失败之后的原因
reason = null;
// 存储成功回调函数
onFulfilledCallbacks = [];
// 存储失败回调函数
onRejectedCallbacks = [];
// resolve和reject为什么要用箭头函数?
// 如果直接调用的话,普通函数this指向的是window或者undefined
// 用箭头函数就可以让this指向当前实例对象
// 也可以 let _this = this
// 更改成功后的状态
resolve = (value) => {
// 只有状态是等待,才执行状态修改
if (this.status === PENDING) {
// 状态修改为成功
this.status = FULFILLED;
// 保存成功之后的值
this.value = value;
// resolve里面将所有成功的回调拿出来执行
while (this.onFulfilledCallbacks.length) {
// Array.shift() 取出数组第一个元素,然后()调用,shift不是纯函数,取出后,数组将失去该元素,直到数组为空
this.onFulfilledCallbacks.shift()(value)
}
}
}
// 更改失败后的状态
reject = (reason) => {
// 只有状态是等待,才执行状态修改
if (this.status === PENDING) {
// 状态成功为失败
this.status = REJECTED;
// 保存失败后的原因
this.reason = reason;
// resolve里面将所有失败的回调拿出来执行
while (this.onRejectedCallbacks.length) {
this.onRejectedCallbacks.shift()(reason)
}
}
}
then(onFulfilled, onRejected) {
const realOnFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
const realOnRejected = typeof onRejected === 'function' ? onRejected : reason => {throw reason};
// 为了链式调用这里直接创建一个 MyPromise,并在后面 return 出去
const promise2 = new MyPromise((resolve, reject) => {
const fulfilledMicrotask = () => {
// 创建一个微任务等待 promise2 完成初始化
queueMicrotask(() => {
try {
// 获取成功回调函数的执行结果
const x = realOnFulfilled(this.value);
// 传入 resolvePromise 集中处理
resolvePromise(promise2, x, resolve, reject);
} catch (error) {
reject(error)
}
})
}
const rejectedMicrotask = () => {
// 创建一个微任务等待 promise2 完成初始化
queueMicrotask(() => {
try {
// 调用失败回调,并且把原因返回
const x = realOnRejected(this.reason);
// 传入 resolvePromise 集中处理
resolvePromise(promise2, x, resolve, reject);
} catch (error) {
reject(error)
}
})
}
// 判断状态
if (this.status === FULFILLED) {
fulfilledMicrotask()
} else if (this.status === REJECTED) {
rejectedMicrotask()
} else if (this.status === PENDING) {
// 等待
// 因为不知道后面状态的变化情况,所以将成功回调和失败回调存储起来
// 等到执行成功失败函数的时候再传递
this.onFulfilledCallbacks.push(fulfilledMicrotask);
this.onRejectedCallbacks.push(rejectedMicrotask);
}
})
return promise2;
}
catch (onRejected) {
// 只需要进行错误处理
this.then(undefined, onRejected);
}
finally (fn) {
return this.then((value) => {
return MyPromise.resolve(fn()).then(() => {
return value;
});
}, (error) => {
return MyPromise.resolve(fn()).then(() => {
throw error
});
});
}
// resolve 静态方法
static resolve (parameter) {
// 如果传入 MyPromise 就直接返回
if (parameter instanceof MyPromise) {
return parameter;
}
// 转成常规方式
return new MyPromise(resolve => {
resolve(parameter);
});
}
// reject 静态方法
static reject (reason) {
return new MyPromise((resolve, reject) => {
reject(reason);
});
}
static all (promiseList) {
return new MyPromise((resolve, reject) => {
const result = [];
const length = promiseList.length;
let count = 0;
if (length === 0) {
return resolve(result);
}
promiseList.forEach((promise, index) => {
MyPromise.resolve(promise).then((value) => {
count++;
result[index] = value;
if (count === length) {
resolve(result);
}
}, (reason) => {
reject(reason);
});
});
});
}
static allSettled = (promiseList) => {
return new MyPromise((resolve) => {
const length = promiseList.length;
const result = [];
let count = 0;
if (length === 0) {
return resolve(result);
} else {
for (let i = 0; i < length; i++) {
const currentPromise = MyPromise.resolve(promiseList[i]);
currentPromise.then((value) => {
count++;
result[i] = {
status: 'fulfilled',
value: value
}
if (count === length) {
return resolve(result);
}
}, (reason) => {
count++;
result[i] = {
status: 'rejected',
reason: reason
}
if (count === length) {
return resolve(result);
}
});
}
}
});
}
static race (promiseList) {
return new MyPromise((resolve, reject) => {
const length = promiseList.length;
if (length === 0) {
return resolve();
} else {
for (let i = 0; i < length; i++) {
MyPromise.resolve(promiseList[i]).then((value) => {
return resolve(value);
}, (reason) => {
return reject(reason);
});
}
}
});
}
}
function resolvePromise(promise, x, resolve, reject) {
// 如果 promise 和 x 指向同一对象,以 TypeError 为据因拒绝执行 promise
// 这是为了防止死循环
if (promise === x) {
return reject(new TypeError('The promise and the return value are the same'));
}
if (typeof x === 'object' || typeof x === 'function') {
// 这个坑是跑测试的时候发现的,如果x是null,应该直接resolve
if (x === null) {
return resolve(x);
}
let then;
try {
// 把 x.then 赋值给 then
then = x.then;
} catch (error) {
// 如果取 x.then 的值时抛出错误 e ,则以 e 为据因拒绝 promise
return reject(error);
}
// 如果 then 是函数
if (typeof then === 'function') {
let called = false;
// 将 x 作为函数的作用域 this 调用之
// 传递两个回调函数作为参数,第一个参数叫做 resolvePromise ,第二个参数叫做 rejectPromise
// 名字重名了,我直接用匿名函数了
try {
then.call(
x,
// 如果 resolvePromise 以值 y 为参数被调用,则运行 [[Resolve]](promise, y)
y => {
// 如果 resolvePromise 和 rejectPromise 均被调用,
// 或者被同一参数调用了多次,则优先采用首次调用并忽略剩下的调用
// 实现这条需要前面加一个变量called
if (called) return;
called = true;
resolvePromise(promise, y, resolve, reject);
},
// 如果 rejectPromise 以据因 r 为参数被调用,则以据因 r 拒绝 promise
r => {
if (called) return;
called = true;
reject(r);
});
} catch (error) {
// 如果调用 then 方法抛出了异常 e:
// 如果 resolvePromise 或 rejectPromise 已经被调用,则忽略之
if (called) return;
// 否则以 e 为据因拒绝 promise
reject(error);
}
} else {
// 如果 then 不是函数,以 x 为参数执行 promise
resolve(x);
}
} else {
// 如果 x 不为对象或者函数,以 x 为参数执行 promise
resolve(x);
}
}
MyPromise.deferred = function () {
var result = {};
result.promise = new MyPromise(function (resolve, reject) {
result.resolve = resolve;
result.reject = reject;
});
return result;
}
module.exports = MyPromise