背景:如果我们要快速背诵并默写出来promise的实现,应该怎么记忆最快。
在默写实现promise之前,先写一遍promise的基本使用方法,主体,then,catch,all,race,清楚我们要实现什么东西
- 主题异步
- 多个then,then里面有成功回调和catch回调,有返回值
- catch函数
- all函数
const p = new MyPromise((resolve, reject) => {
setTimeout(() => {
console.log(111);
resolve("ok");
}, 1000);
});
p.then(
(data) => {
console.log(987879);
return "1";
},
(error) => {
console.log(error);
return "2";
}
);
p.then(
(data) => {
console.log(data, 798798);
return "1";
},
(error) => {
console.log(error);
return "2";
}
)
.then()
.then(() => {
console.log(666);
});
整体架构
由于promise实现比较长,所以,每一次默写,应该是先搭建架子,然后默写注释每个函数做了什么,
一:搭建架子
主体,then,catch
二:主体要做什么
- 内部值定义:状态,结果,then的回调函数存储,resolve,reject函数
- 执行回调函数,trycatch兜底
resolve,reject函数
- 状态变更
- 保存结果
- 执行回调函数(微任务)
三:then要做什么
- 返回promise
- promise的结果结果then执行的结果而定:promise,非promise,报错
- 状态可能是等待状态和有结果状态,有结果执行执行回调函数,等待保存回调函数
- 回调函数可以不传
- 微任务环境下执行
四:catch要做什么
为then功能的一部分,就是reject的那部分
构造函数主体做了什么(new的时候)
- 传入一个函数fn,这个函数有两个参数,resolve,reject,这两个参数也都是函数。
- new的时候,立即执行这个函数,同步执行的。
- 定义两个床给fn的函数resolve,reject,这两个函数是promise内部传给使用者的,这两个函数都接受一个参数data,这个参数是结果。
- 定义一个promise的状态status,初始值为pedding,定义promise的结果result,初始值为null。
- 传入的函数fn抛异常的时候,能捕捉到,状态会变成reject,所以要用try catch。
resolve和reject函数做了设么
- 修改状态为成功/失败
- 设置当前promise执行完的结果值result,以供后面使用
- 执行存储的回调函数
- 状态只能修改一次,所以修改状态之前要先判断
- 微任务异步执行
then方法做了什么
-
是原型方法
-
接收两个参数,都是函数,前一个是成功时候的回调,后一个是失败时候的回调
-
如果状态是成功,立即执行成功回调
-
如果状态是失败,立即执行失败回调
-
如果状态是等待(异步情况),把成功回调函数和失败函数存起来,等状态改变后,由resolve或者reject函数执行。并且由于promise成功后,是可以执行多个then的,所以应该把成功回调函数存成数组。
-
then方法会返回一个新的promise,promise的结果由传入的回调函数的结果而定。分别有四种情况:
a. 没有return结果,是understand,那么则把promise结果设置为成功,值为undefined。
b. 有return结果,return结果只要不是promise,则同样把promise设置为成功,接值为该return的结果。
c. 有return结果,结果为一个新的promise,则以这个新的promise的结果为结果,新的promise是成功的,则返回的promise是成功,新的promise结果是失败的,则返回的promise失败。
d. 如果回调函数在执行过程中报错,则返回失败的promise,所以应该用try catch包裹一下。
e. 注意:即便我们最初的promise的执行结果是失败的,但是被reject函数兜住以后,结果仍为成功,除非新的回调函数内返回的的promise为失败才可能是再次return失败。
-
异步的情况,then不能直接得到promise的结果,需要等待异步的结果,再执行新的promise的resolve函数或者reject函数,为了达到次目标,存起来的函数不应该直接为onResolved/onRejected这两个回调函数,应该在外包装一层函数,这一层函数内执行onResolved/onRejected函数,同时根据结果执行新的返回的promi的resolve/reject函数。
-
then方法允许用户不传参数,且依然要有返回一个promise,由于then是链式调用,所以then方法即便没有传入参数,也要默认给一个成功回调和一个失败回调,这个回调的参数是promise的结果。
-
promise的then方法异步微任务执行的,优先用queueMicrotask函数模拟,次选setTimeout(宏任务)
then方法里的两个函数参数onResolved,onRejected做了什么
- 这两个函数可能同步执行(当promise状态被同步修改掉,没有写异步代码时),也可能异步执行。
- 这两个函数传入的参数是promise的结果,所以这就是为什么之前结果要保存下来。
catch方法做了什么
- catch也是返回一个promise,但是一定是失败的。
- catch的功能是then功能的一部分,所以调用then方法就行。
Promise.resolve方法做了什么
-
是静态方法
-
也是返回一个Promise,这个Promise的结果:
a. 如果Promise.resolve的参数是一个promise对象,则根据这个promise的对象的结果来定
b. 如果参数不是promise对象,则无论返回什么,都是成功
c. 如果参数执行过程中报错,则返回失败
d. 返回的promise的结果的参数,就是调用的时候传入的参数,如果传入的是函数,则是函数内部return的值。
Promise.resolve方法做了什么
- 是静态方法
- 也是返回一个Promise,这个Promise的结果,永远是reject,不论传入了什么。
Promise.all方法做了什么
- 是静态方法
- 返回的结果也是promise对象
- 如果所有promise都成功,则返回成功,成功的参数是一个数组,里面按照执行顺序,保存了每一个promise的成功结果。
- 如果失败,则返回那个失败的结果。
Promise.race方法做了什么
- 是静态方法
- 返回的结果也是promise对象
- 只返回最快得到结果的那个promise,那个promise是成功就返回成功,否则返回失败
完整版promise
function MyPromise(fn) {
this.PEDDING = "pedding";
this.FULFILLED = "fulfilled";
this.REJECTED = "rejected";
this.result = null;
this.status = "pedding";
this.callbacks = [];
const resolve = (data) => {
if (this.status !== this.PEDDING) return;
this.status = this.FULFILLED;
this.result = data;
queueMicrotask(() => {
this.callbacks.forEach((item) => {
item.onResolved(this.result);
});
});
};
const reject = (data) => {
if (this.status !== this.PEDDING) return;
this.status = this.REJECTED;
this.result = data;
queueMicrotask(() => {
this.callbacks.forEach((item) => {
item.onRejected(this.result);
});
});
};
// 立即执行
try {
fn(resolve, reject);
} catch (error) {
reject(error);
}
}
MyPromise.prototype.then = function (onResolved, onRejected) {
const self = this;
if (typeof onResolved !== "function") {
onResolved = (data) => {
return data;
};
}
if (typeof onRejected !== "function") {
onRejected = (err) => {
return err;
};
}
return new MyPromise((resolve, reject) => {
// 回调
const callback = (type) => {
try {
const result = type(self.result);
if (result instanceof MyPromise) {
result.then(
(v) => {
resolve(v);
},
(e) => {
reject(e);
}
);
} else {
resolve(self.result);
}
} catch (error) {
reject(error);
}
};
// 回调函数,同步执行的情况
if (this.status === this.FULFILLED) {
queueMicrotask(() => {
callback(this.onResolved);
});
}
if (this.status === this.REJECTED) {
queueMicrotask(() => {
callback(this.onRejected);
});
}
// 异步情况
if (this.status === this.PEDDING) {
this.callbacks.push({
onResolved() {
callback(onResolved);
},
onRejected() {
callback(onRejected);
},
});
}
});
};
MyPromise.prototype.catch = function(errFn) {
return this.then(undefined, errFn)
}
MyPromise.resolve = function (payload) {
return new MyPromise((resolve, reject) => {
if (payload instanceof MyPromise) {
payload.then(
(data) => {
resolve(data);
},
(err) => {
reject(err);
}
);
} else {
resolve(payload);
}
});
};
MyPromise.reject = function (payload) {
return new MyPromise((resolve, reject) => {
reject(payload);
});
};
MyPromise.all = function (promises) {
return new MyPromise((resolve, reject) => {
let count = 0;
const results = [];
for (let i = 0; i < promises.length; i++) {
const element = promises[i];
element.then(
(data) => {
count++;
results[i] = data;
if (count === promises.length) {
resolve(results);
}
},
(err) => {
reject(err);
}
);
}
});
};
MyPromise.race = function (promises) {
return new MyPromise((resolve, reject) => {
for (let i = 0; i < promises.length; i++) {
const element = promises[i];
element.then(
(data) => {
resolve(data);
},
(err) => {
reject(err);
}
);
}
});
};