# 手写Promise
# 基本原理
Promise是一个类,在执行这个类时传入一个回调函数,这个回调函数将会立即执行。
Promise会有三种状态:
Pending 等待
Fulfilled 完成
Rejected 失败
状态只能由Pending --> Fulfilled 或者 Pending --> Rejected,且一旦发生改变便不可二次修改;
Promise中使用resolve 和 reject 两个函数来更改状态;
then 方法内部做的事情是状态判断
如果状态是成功,调用成功回调函数。
如果状态是失败,调用失败回调函数。
# 实现过程
创建MyPromise这个类,传入回调函数callback
// 创建MyPromise class class MyPromise{ // 传入回调函数 constructor(callback){ // 立即执行 callback(); } }
callback传入resolve和reject方法
// 定义常量表示状态 const PENDING = "pending"; const FULFILLED = "fulfilled"; const REJECTED = "rejected"; // 创建MyPromise class class MyPromise { // 传入回调函数 constructor(callback) { // 立即执行 callback(this.resolve, this.reject); } // 初始化状态 status = PENDING; // 状态转变为成功的值 value = null; // 状态转变为失败的值 reason = null; /** * 在这里是用箭头函数,可以指向实例对象 * 而普通函数会指向window或者undefined; */ // 更改成功后的状态 resolve = (val) => { if (this.status === PENDING) { this.status = FULFILLED; this.value = val; } }; // 更改失败后的状态 reject = (val) => { if (this.status === PENDING) { this.status = REJECTED; this.reason = val; } }; }
then的简单实现
/** * 两个参数 * 状态为成功时返回调用第一个 * 状态为失败时返回调用第二个 * */ then = (resCallback, rejCallback) => { // 判断状态 if (this.status == FULFILLED) { resCallback(this.resolve); } else if (this.status == REJECTED) { rejCallback(this.reject); } }
完整代码
// 定义常量表示状态 const PENDING = "pending"; const FULFILLED = "fulfilled"; const REJECTED = "rejected"; // 创建MyPromise class class MyPromise { // 传入回调函数 constructor(callback) { // 立即执行 callback(this.resolve, this.reject); } // 初始化状态 status = PENDING; // 状态转变为成功的值 value = null; // 状态转变为失败的值 reason = null; /** * 在这里是用箭头函数,可以指向实例对象 * 而普通函数会指向window或者undefined; */ // 更改成功后的状态 resolve = (val) => { if (this.status === PENDING) { this.status = FULFILLED; this.value = val; } }; // 更改失败后的状态 reject = (val) => { if (this.status === PENDING) { this.status = REJECTED; this.reason = val; } }; /** * 两个参数 * 状态为成功时返回调用第一个 * 状态为失败时返回调用第二个 * */ then = (resCallback, rejCallback) => { // 判断状态 if (this.status == FULFILLED) { resCallback(this.value); } else if (this.status == REJECTED) { rejCallback(this.reason); } }; } let promise = new MyPromise((resolve,reject)=>{ resolve("我成功了"); }) promise.then((res)=>{ console.log(res); // 我成功了 })
# 加入异步逻辑
如果在promise实例中,以setTimout异步逻辑来写入resolve或reject,则在then中不会有任何响应。因为then是立即执行,在执行时status仍然处于PENDING状态。
// 定义常量表示状态
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";
// 创建MyPromise class
class MyPromise {
// 传入回调函数
constructor(callback) {
// 立即执行
callback(this.resolve, this.reject);
}
// 初始化状态
status = PENDING;
// 存储成功回调
onFulfilledCallbacks = null;
// 存储失败回调
onRejectedCallbacks = null;
// 状态转变为成功的值
value = null;
// 状态转变为失败的值
reason = null;
/**
* 在这里是用箭头函数,可以指向实例对象
* 而普通函数会指向window或者undefined;
*/
// 更改成功后的状态
resolve = (val) => {
if (this.status === PENDING) {
this.status = FULFILLED;
this.value = val;
// 若实例中执行的是异步逻辑
this.onFulfilledCallbacks && this.onFulfilledCallbacks(val);
}
};
// 更改失败后的状态
reject = (val) => {
if (this.status === PENDING) {
this.status = REJECTED;
this.reason = val;
// 若实例中执行的是异步逻辑
this.onRejectedCallbacks && this.onRejectedCallbacks(val);
}
};
/**
* 两个参数
* 状态为成功时返回调用第一个
* 状态为失败时返回调用第二个
* */
then = (onFulfilled, onRejected) => {
console.log("this.status", this.status);
// 判断状态
if (this.status == FULFILLED) {
onFulfilled(this.value);
} else if (this.status == REJECTED) {
onRejected(this.reason);
} else if (this.status == PENDING) {
/**
* 此处有修改,因then立即执行,则状态为pending,将成功和失败的回调存储起来。
* 若干秒后,在resolve后reject函数中执行存储函数
* */
this.onFulfilledCallbacks = onFulfilled;
this.onRejectedCallbacks = onRejected;
}
};
}
let promise = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve("222我成功了");
}, 2000);
});
promise.then(
(res) => {
console.log("resolve", res); // 222我成功了
},
(reason) => {
console.log("reject", reason);
}
);
# 存储多次回调
当使用多次promise.then时,只有最后一次的回调函数实行了,因为onFulfilledCallbacks 存储函数,只存储了最后一次的回调。因此需要改成数组形式。
promise.then(
(res) => {
console.log("1");
console.log("resolve", res); // success
},
);
promise.then(
(res) => {
console.log("2");
console.log("resolve", res); // success
},
);
promise.then(
(res) => {
console.log("3");
console.log("resolve", res); // success
},
);
// ---最终打印结果---
// 3
// success
先修改成数组形式,并在resolve,reject函数中做逻辑修改。
// 存储函数的变化
// 存储成功回调
// onFulfilledCallbacks = null;
onFulfilledCallbacks = [];
// 存储失败回调
// onRejectedCallbacks = null;
onRejectedCallbacks = [];
// 更改成功后的状态
resolve = (val) => {
if (this.status === PENDING) {
this.status = FULFILLED;
this.value = val;
// 若实例中执行的是异步逻辑
this.onFulfilledCallbacks.forEach((item) => item());
this.onFulfilledCallbacks = []; // 循环完置空
}
};
// 更改失败后的状态
reject = (val) => {
if (this.status === PENDING) {
this.status = REJECTED;
this.value = val;
// 若实例中执行的是异步逻辑
this.onRejectedCallbacks.forEach((item) => item());
this.onRejectedCallbacks = []; // 循环完置空
}
};
// 打印结果
// 1
// resolve success
// 2
// resolve success
// 3
// resolve success
# 如何做到链式调用
- 为了达到链式调用,应在第一个then里返回promise。我们就返回一个promise2:
promise2 = new Promise((resolve,reject)=>{})
- 我们规定第一个then返回的值,称之为x,判断x的函数叫做resolvePromise.
逻辑梳理:
- 判断x是否为promise(使用instanceof判断)
- 如果是promise,则取结果,作为新的promise2成功的结果。
// 改变 状态为FULFILLED时的代码逻辑。
then(onFulfilled, onRejected) {
// 为了链式调用,这里直接创建一个 MyPromise, 并在后面 return 出去
const promise2 = new MyPromise((resolve, reject) => {
// 判断状态
if (this.status == FULFILLED) {
// 需要改变的地方,第一个then返回值称之为x
let x = onFulfilled(this.value);
// resolvePromise函数,处理自己return的promise和默认的promise2的关系
resolvePromise(promise2, x, resolve, reject);
onFulfilled(this.value);
} else if (this.status == REJECTED) {
onRejected(this.reason);
} else if (this.status == PENDING) {
/**
* 此处有修改,因then立即执行,则状态为pending,将成功和失败的回调存储起来。
* 若干秒后,在resolve后reject函数中执行存储函数
* */
this.onFulfilledCallbacks.push(() => {
return onFulfilled(this.value);
});
this.onRejectedCallbacks.push(() => {
return onRejected(this.reason);
});
}
});
return promise2;
}
}
# 完成resolvePromise函数
让不同的promise代码互相套用,叫做resolvePromise
- 如果 x === promise2,则会造成循环引用,自己等待自己完成,则报错“循环引用”。
let p = new Promise(resolve=>{
resolve(0)
});
let p2 = p.then(data => {
// 循环引用,自己等待自己完成
return p2;
})
# 判断x的必要条件
- x不能为null
- 若为普通值,则resolve(x)
- 声明了then
- 如果取then报错,则走reject()
- 如果then是个函数,则call执行then,第一个参数为this,后面是成功的回调和失败的回调。
- 如果成功的回调还是promise,就递归继续解析
- 成功和失败只能调用一个,所以设定一个called来防止多次调用。
// 定义常量表示状态
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";
// 创建MyPromise class
class MyPromise {
// 传入回调函数
constructor(executor) {
// 立即执行
executor(this.resolve, this.reject);
}
// 初始化状态
status = PENDING;
// 存储成功回调
// onFulfilledCallbacks = null;
onFulfilledCallbacks = [];
// 存储失败回调
// onRejectedCallbacks = null;
onRejectedCallbacks = [];
// 状态转变为成功的值
value = null;
// 状态转变为失败的值
reason = null;
/**
* 在这里是用箭头函数,可以指向实例对象
* 而普通函数会指向window或者undefined;
*/
// 更改成功后的状态
resolve = (val) => {
if (this.status === PENDING) {
this.status = FULFILLED;
this.value = val;
// 若实例中执行的是异步逻辑
this.onFulfilledCallbacks.forEach((item) => item());
this.onFulfilledCallbacks = []; // 循环完置空
}
};
// 更改失败后的状态
reject = (val) => {
if (this.status === PENDING) {
this.status = REJECTED;
this.value = val;
// 若实例中执行的是异步逻辑
this.onRejectedCallbacks.forEach((item) => item());
this.onRejectedCallbacks = []; // 循环完置空
}
};
/**
* 两个参数
* 状态为成功时返回调用第一个
* 状态为失败时返回调用第二个
* */
then(onFulfilled, onRejected) {
// 为了链式调用,这里直接创建一个 MyPromise, 并在后面 return 出去
// 以下是立即执行的
const promise2 = new MyPromise((resolve, reject) => {
// 判断状态
if (this.status == FULFILLED) {
// 需要改变的地方,第一个then返回值称之为x
let x = onFulfilled(this.value);
// resolvePromise函数,处理自己return的promise和默认的promise2的关系
resolvePromise(promise2, x, resolve, reject);
} else if (this.status == REJECTED) {
onRejected(this.reason);
} else if (this.status == PENDING) {
/**
* 此处有修改,因then立即执行,则状态为pending,将成功和失败的回调存储起来。
* 若干秒后,在resolve后reject函数中执行存储函数
* */
this.onFulfilledCallbacks.push(() => {
return onFulfilled(this.value);
});
this.onRejectedCallbacks.push(() => {
return onRejected(this.reason);
});
}
});
return promise2;
}
}
// 循环引用报错
// let p = new Promise(resolve=>{
// resolve(0)
// });
// let p2 = p.then(data => {
// // 循环引用,自己等待自己完成
// return p2;
// })
function resolvePromise(promise2, x, resolve, reject) {
// 循环引用报错
if (x === promise2) {
// reject报错
return reject(new TypeError("chaining cycle detected for promise"));
}
// 防止多次调用
let called;
// x不是null 且x是对象或者函数
if (x != null && (typeof x === "object" || typeof x === "function")) {
try {
// A+规定, 声明then = x的then方法
let then = x.then;
// 如果then是函数,就默认是promise
if (typeof then == "function") {
// 让then执行 第一个参数是this 后面为成功回调 和 失败回调
// y就是then中的callback
then.call(
x,
(y) => {
// 成功和失败只能调用一个
if (called) return;
called = true;
// resolve的结果依旧是promise 那就继续解析
resolvePromise(promise2, y, resolve, reject);
},
(err) => {
// 成功和失败只能调用一个
if (called) return;
called = true;
reject(err); // 失败了便报错
}
);
} else {
resolve(x); // 直接成功即可
}
} catch (e) {
// 也属于失败
if (called) return;
called = true;
// 取then出错了即不再继续执行
reject(e);
}
} else {
// 若为普通值,则直接返回x
resolve(x);
}
}
# 为参数设置可选
then中的两个参数,如果不是函数,则直接返回value
then(onFulfilled, onRejected) {
// onFulfilled如果不是函数,就忽略onFulfilled,直接返回value
onFulfilled = typeof onFulfilled === "function" ? onFulfilled : (value) => value;
// onRejected如果不是函数,就忽略onRejected,直接扔出错误
onRejected = typeof onRejected === "function" ? onRejected : (err) => {throw err;};
// ...
}
且切记一点,onFulfilled和onRejected不能同步被调用,必须异步调用。我们用setTimeout解决异步问题。
const promise2 = new MyPromise((resolve, reject) => {
// 判断状态
if (this.status == FULFILLED) {
setTimeout(() => {
try {
// 需要改变的地方,第一个then返回值称之为x
let x = onFulfilled(this.value);
// resolvePromise函数,处理自己return的promise和默认的promise2的关系
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
console.log(e);
}
}, 0);
} else if (this.status == REJECTED) {
setTimeout(() => {
try {
// 需要改变的地方,第一个then返回值称之为x
let x = onRejected(this.reason);
// resolvePromise函数,处理自己return的promise和默认的promise2的关系
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
console.log(e);
}
}, 0);
} else if (this.status == PENDING) {
/**
* 此处有修改,因then立即执行,则状态为pending,将成功和失败的回调存储起来。
* 若干秒后,在resolve后reject函数中执行存储函数
* */
this.onFulfilledCallbacks.push(() => {
setTimeout(() => {
try {
// 需要改变的地方,第一个then返回值称之为x
let x = onFulfilled(this.value);
// resolvePromise函数,处理自己return的promise和默认的promise2的关系
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
console.log(e);
}
}, 0);
});
this.onRejectedCallbacks.push(() => {
setTimeout(() => {
try {
// 需要改变的地方,第一个then返回值称之为x
let x = onRejected(this.value);
// resolvePromise函数,处理自己return的promise和默认的promise2的关系
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
console.log(e);
}
}, 0);
});
}
});
# 完整代码
// 定义常量表示状态
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";
// 创建MyPromise class
class MyPromise {
// 传入回调函数
constructor(executor) {
// 立即执行
executor(this.resolve, this.reject);
}
status = PENDING; // 初始化状态
onFulfilledCallbacks = []; // 存储成功回调
onRejectedCallbacks = []; // 存储失败回调
value = null; // 状态转变为成功的值
reason = null; // 状态转变为失败的值
/**
* 在这里是用箭头函数,可以指向实例对象
* 而普通函数会指向window或者undefined;
*/
// 更改成功后的状态
resolve = (val) => {
if (this.status === PENDING) {
this.status = FULFILLED;
this.value = val;
// 若实例中执行的是异步逻辑
this.onFulfilledCallbacks.forEach((item) => item());
this.onFulfilledCallbacks = []; // 循环完置空
}
};
// 更改失败后的状态
reject = (val) => {
if (this.status === PENDING) {
this.status = REJECTED;
this.value = val;
// 若实例中执行的是异步逻辑
this.onRejectedCallbacks.forEach((item) => item());
this.onRejectedCallbacks = []; // 循环完置空
}
};
/**
* 两个参数
* 状态为成功时返回调用第一个
* 状态为失败时返回调用第二个
* */
then(onFulfilled, onRejected) {
// onFulfilled如果不是函数,就忽略onFulfilled,直接返回value
onFulfilled =
typeof onFulfilled === "function" ? onFulfilled : (value) => value;
// onRejected如果不是函数,就忽略onRejected,直接扔出错误
onRejected =
typeof onRejected === "function"
? onRejected
: (err) => {
throw err;
};
// 为了链式调用,这里直接创建一个 MyPromise, 并在后面 return 出去
// 以下是立即执行的
const promise2 = new MyPromise((resolve, reject) => {
// 判断状态
if (this.status == FULFILLED) {
setTimeout(() => {
try {
// 需要改变的地方,第一个then返回值称之为x
let x = onFulfilled(this.value);
// resolvePromise函数,处理自己return的promise和默认的promise2的关系
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
console.log(e);
}
}, 0);
} else if (this.status == REJECTED) {
setTimeout(() => {
try {
// 需要改变的地方,第一个then返回值称之为x
let x = onRejected(this.reason);
// resolvePromise函数,处理自己return的promise和默认的promise2的关系
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
console.log(e);
}
}, 0);
} else if (this.status == PENDING) {
/**
* 此处有修改,因then立即执行,则状态为pending,将成功和失败的回调存储起来。
* 若干秒后,在resolve后reject函数中执行存储函数
* */
this.onFulfilledCallbacks.push(() => {
setTimeout(() => {
try {
// 需要改变的地方,第一个then返回值称之为x
let x = onFulfilled(this.value);
// resolvePromise函数,处理自己return的promise和默认的promise2的关系
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
console.log(e);
}
}, 0);
});
this.onRejectedCallbacks.push(() => {
setTimeout(() => {
try {
// 需要改变的地方,第一个then返回值称之为x
let x = onRejected(this.value);
// resolvePromise函数,处理自己return的promise和默认的promise2的关系
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
console.log(e);
}
}, 0);
});
}
});
return promise2;
}
}
function resolvePromise(promise2, x, resolve, reject) {
// 循环引用报错
if (x === promise2) {
// reject报错
return reject(new TypeError("chaining cycle detected for promise"));
}
// 防止多次调用
let called;
// x不是null 且x是对象或者函数
if (x != null && (typeof x === "object" || typeof x === "function")) {
try {
// A+规定, 声明then = x的then方法
let then = x.then;
// 如果then是函数,就默认是promise
if (typeof then == "function") {
// 让then执行 第一个参数是this 后面为成功回调 和 失败回调
// y就是then中的callback
then.call(
x,
(y) => {
// 成功和失败只能调用一个
if (called) return;
called = true;
// resolve的结果依旧是promise 那就继续解析
resolvePromise(promise2, y, resolve, reject);
},
(err) => {
// 成功和失败只能调用一个
if (called) return;
called = true;
reject(err); // 失败了便报错
}
);
} else {
resolve(x); // 直接成功即可
}
} catch (e) {
// 也属于失败
if (called) return;
called = true;
// 取then出错了即不再继续执行
reject(e);
}
} else {
// 若为普通值,则直接返回x
resolve(x);
}
}
let promise = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve("success");
}, 2000);
});
function other() {
return new MyPromise((resolve, reject) => {
resolve("other");
});
}
function other2() {
return new MyPromise((resolve, reject) => {
resolve("other2");
});
}
promise
.then((res) => {
console.log("resolve", res); // success
return other();
})
.then((res) => {
console.log("resolve2", res); // resolve2 other
return other2();
})
.then((res) => {
console.log("resolve3", res); // resolve3 toher2
});
# 手写思路总结
- 标明三种状态:Pending、Fulfilled、Rejected
- Promise传入1个参数,该参数为callback,参数为resolve和reject,这两个值为可选值。
- then函数传入2个参数,第一个是状态成功后的callback,第二个是状态失败后的回调。then只会返回一种状态。
- 考虑到异步逻辑在Promise的callback中,因此成功函数和失败函数需要存储,以数组形式保存。等到异步操作执行完,再挨个执行成功/失败函数。
- 为then做链式调用,需要then中返回一个MyPromise实例,称之为promise2。
- 设x为第一个成功时的回调函数,编写一个resolvePromise来判断x,为then的下一步编写逻辑。
- x不能为null,当x与Promise2全等时,将进入循环,这种情况将抛出错误。
- 当x有then方法,且then方法的类型为function时,将执行then方法,指向x。
- 需要设置一个called,表示状态是否已定,pending定向了Fulfilled,或是定向了Rejected。
- 因为onFulfiiled和onRejeted不能同时走,需要在promise2函数中每一步做setTimout异步处理。
← 对象合并 手写promiseAll →