前言
- 本文会从零开始带你手撕一个简易的 Promise。
- 我把 Promise 的实现拆分为如下几个步骤, 每个步骤从基本使用到代码实现, 一点一点地实现一个完整 Promise:
-
- Promise 初见雏形
-
- then 方法初见雏形
-
- 执行器函数中出现异步代码
-
- then 方法接收的回调中返回普通值
-
- then 方法接收的回调中抛出错误
-
- then 方法接收的回调中返回自己
-
- then 方法接收的回调中返回新的 promise
-
- resolve 方法中传入 promise
-
本文最后附上该文章实现的简易 Promise 完整代码~~~
1. Promise 初见雏形
基本使用
const { MyPromise } = require("./MyPromise");
let p1 = new MyPromise((resolve, reject) => {
resolve("成功了");
// reject('失败了')
// throw new Error('出错了')
});
console.log(p1);
手写实现
const PENDING = "PENDING";
const RESOLVE = "RESOLVE";
const REJECT = "REJECT";
class MyPromise {
constructor(executor) {
this.status = PENDING;
this.value = undefined;
this.reason = undefined;
let resolve = (value) => {
if (this.status === PENDING) {
this.status = RESOLVE;
this.value = value;
}
};
let reject = (reason) => {
if (this.status === PENDING) {
this.status = REJECT;
this.reason = reason;
}
};
try {
executor(resolve, reject);
} catch (error) {
reject(error);
}
}
}
module.exports = {
MyPromise,
};
2. then 方法初见雏形
基本使用
// const {
// MyPromise
// } = require('./MyPromise')
let p1 = new Promise((resolve, reject) => {
resolve("成功了");
// reject('失败了')
// throw new Error('出错了')
});
p1.then(
(value) => {
console.log(value);
},
(reason) => {
console.log(reason);
}
);
p1.then(
(value) => {
console.log(value);
},
(reason) => {
console.log(reason);
}
);
上述代码中通过 p1 调用了两次 then 方法, 他们都会执行
手写实现
class MyPromise {
// ......
+ // 原型上的方法, 供给实例调用
+ then(onFulfilled, onRejected) {
+ if (this.status === RESOLVE) {
+ onFulfilled(this.value);
+ }
+
+ if (this.status === REJECT) {
+ onRejected(this.reason);
+ }
+ }
}
3. 执行器函数中出现异步代码
基本使用
import MyPromise from "./MyPromise";
let p1 = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve("成功了");
// reject('失败了')
// throw new Error("出错了");
}, 2000);
});
p1.then(
(value) => {
console.log(value);
},
(reason) => {
console.log(reason);
}
);
- 先来看下上面的代码,在执行器函数中出现了异步代码
setTimeout(),如果使用面我们目前实现的MyPromise,那么会发现什么都没有输出 - 这是因为通过
p1调用了then方法,此时 p1 这个 promise 实例的状态仍还没有发生改变,而是两秒后改变。即仍为 pending 状态,所以我们需要在源码实现中维护相应的回调, 等到可以调用时再取出回调然后执行。
手写实现
class MyPromise {
constructor(executor) {
this.status = PENDING
this.value = undefined
this.reason = undefined
+ // 维护成功的回调
+ this.onResolvedCallbacks = []
+ // 维护失败的回调
+ this.onRejectedCallbacks = []
let resolve = (value) => {
if (this.status === PENDING) {
this.status = RESOLVE
this.value = value
+ // 等到异步代码执行完毕后,再取出相应的回调然后执行
+ this.onResolvedCallbacks.forEach(fn => fn())
}
}
let reject = (reason) => {
if (this.status === PENDING) {
this.status = REJECT
this.reason = reason
+ // 等到异步代码执行完毕后,再取出相应的回调然后执行
+ this.onRejectedCallbacks.forEach(fn => fn())
}
}
// ......
}
then(onFulfilled, onRejected) {
if (this.status === RESOLVE) {
onFulfilled(this.value)
}
if (this.status === REJECT) {
onRejected(this.reason)
}
+ // 由于用户在执行器函数中写了异步代码, 随后调用 then 方法, 此时 promise 实例(p1)的状态还是 PENDING
+ if (this.status === PENDING) {
+ // 将用户传给 then 方法的第一个参数(成功的回调)存起来
+ this.onResolvedCallbacks.push(() => {
+ onFulfilled(this.value)
+ })
+
+ // 将用户传给 then 方法的第一个参数(成功的回调)存起来
+ this.onRejectedCallbacks.push(() => {
+ onRejected(this.reason)
+ })
+ }
}
}
4. then 方法接收的回调中返回 “普通值”
基本使用
- 什么是 “普通值”:除了 promise 实例和抛出错误以外,其他的情况都认为是返回“普通值”
- 如果返回的是 “普通值”,则会走到下一个
then方法第一个参数的回调。
const { MyPromise } = require("./MyPromise");
let p1 = new Promise((resolve, reject) => {
setTimeout(() => {
// 调用 resolve, 则参数会被传递到下一个 then 方法第一个回调的参数
resolve("2秒后失败了");
}, 2000);
// resolve('成功了')
// reject('失败了')
});
let p2 = p1.then(
(value) => {
console.log(value);
// 下面返回 100,就是普通值。如果没有返回值,则是undefined,也算是普通值
// 该 100 会传到下一个 then 方法的第一个回调的参数
return 100;
},
(reason) => {
return -100;
}
);
p2.then(
(value) => {
console.log("value:", value);
},
(reason) => {
console.log("reason", reason);
}
);
手写实现
- 在源码实现中, 我们需要完成两件事:
-
- 由于
p1.then()的返回值p2仍然可以调用then(), 因此得知then()需要返回一个 promise 实例
- 由于
-
- 用户会给
then()传入两个回调, 这两个回调可能会有返回值, 我们需要根据返回值的类型(即 “普通值” or 抛出错误 or 返回 promise)做出不同的处理。
- 用户会给
-
class MyPromise {
// ......
then(onFulfilled, onRejected) {
+ let promise2 = new MyPromise((resolve, reject) => {
if (this.status === RESOLVE) {
+ let x = onFulfilled(this.value)
+ // 注意:这里需要再次调用 resolve, 更新此 promise2 的 value 值,以便继续传递下去。下面同理!
+ resolve(x)
}
if (this.status === REJECT) {
+ let x = onRejected(this.reason)
+ reject(x)
}
if (this.status === PENDING) {
this.onResolvedCallbacks.push(() => {
+ let x = onFulfilled(this.value)
+ resolve(x)
})
this.onRejectedCallbacks.push(() => {
+ let x = onRejected(this.reason)
+ reject(x)
})
}
})
+ return promise2
}
}
5. then 方法接收的回调中抛出错误
基本使用
// let {
// MyPromise
// } = require("./myPromise");
let p1 = new Promise((resolve, reject) => {
resolve("成功啦");
});
let p2 = p1.then(
(data) => {
// 这里抛出错误, 会传递到下一个then回调中第二个参数
throw new Error("error了");
},
(err) => {
return "失败呵呵";
}
);
p2.then(
(data) => {
console.log(data);
},
(err) => {
console.log("出错了-------", err);
}
);
手写实现
-
这一步的实现我把它拆为如下四个步骤:
-
- 定义一个 resolvePromise 方法用于解析返回值
-
- resolvePromise 方法的初步实现
-
- 开启一个定时器
-
- 使用 try...catch 捕获错误
-
+// 2. resolvePromise 方法的初步实现:如果 x 是一个新的 promise,那么通过这个方法,判断该新 promise 是成功还是失败,成功+则调用 promise2 的 resolve, 失败则调用 promise2 的 reject
+const resolvePromise = (promise2, x, resolve, reject) => {
+ console.log(promise2);
+ console.log(x);
+ console.log(resolve);
+ console.log(reject);
+};
class MyPromise {
// ......
then(onFulfilled, onRejected) {
let promise2 = new MyPromise((resolve, reject) => {
// ......
if (this.status === RESOLVED) {
+ // 3. 开启一个定时器:由于下面要将 promise2 传给 resolvePromise(),而 promise2 的创建需要该执行器函数的代码全部执行才能获取到,为了保证能获取到 promise2,需要开启一个定时器
+ setTimeout(() => {
+ // 4. 使用 try...catch 捕获错误:由于上边的第一个try...catch无法捕获到错误,所以需要在这里捕获下
+ try {
+ let x = onFulfilled(this.value);
+
+ // 1. 定义一个 resolvePromise 方法用于解析返回值:该方法会根据用户的返回值,进行相应的状态处理
+ resolvePromise(promise2, x, resolve, reject);
+ } catch (e) {
+ reject(e);
+ }
+ }, 0);
}
if (this.status === REJECTED) {
+ // 3. 开启一个定时器:同理
+ setTimeout(() => {
+ // 4. 使用 try...catch 捕获错误:同理
+ try {
+ let x = onRejected(this.reason);
+
+ // 1. 定义一个 resolvePromise 方法用于解析返回值:同理!
+ resolvePromise(promise2, x, resolve, reject);
+ } catch (e) {
+ reject(e);
+ }
+ }, 0);
}
if (this.status === PENDING) {
this.onResolvedCallbacks.push(() => {
+ // 3. 开启一个定时器:同理
+ setTimeout(() => {
+ // 4. 使用 try...catch 捕获错误:同理
+ try {
+ let x = onFulfilled(this.value);
+
+ // 1. 定义一个 resolvePromise 方法用于解析返回值:同理
+ resolvePromise(promise2, x, resolve, reject);
+ } catch (e) {
+ reject(e);
+ }
+ }, 0);
});
this.onRejectedCallbacks.push(() => {
+ // 3. 开启一个定时器:同理
+ setTimeout(() => {
+ // 4. 使用 try...catch 捕获错误:同理
+ try {
+ let x = onRejected(this.reason);
+
+ // 1. 定义一个 resolvePromise 方法用于解析返回值:同理
+ resolvePromise(promise2, x, resolve, reject);
+ } catch (e) {
+ reject(e);
+ }
+ }, 0);
});
}
});
return promise2;
}
}
6. then 方法接收的回调中返回自己
基本使用
let { MyPromise } = require("./myPromise");
let p1 = new Promise((resolve, reject) => {
resolve("成功啦");
});
let p2 = p1.then(
(data) => {
// then 方法接收的回调中返回自己
return p2;
},
(err) => {
return "失败呵呵";
}
);
p2.then(
(data) => {},
(err) => {
console.log(err);
}
);
手写实现
const resolvePromise = (promise2, x, resolve, reject) => {
+ // 如果用户返回的还是自己,那么就称为循环引用,即自己等待自己
+ if (promise2 === x) {
+ return reject(
+ new TypeError("我的报错:Chaining cycle detected for promise #<Promise")
+ );
+ }
};
7. then 方法接收的回调中返回新的 promise
基本使用
let { MyPromise } = require("./myPromise");
let p1 = new Promise((resolve, reject) => {
resolve("1---成功啦");
});
let p2 = p1.then(
(data) => {
let p3 = new Promise((resolve, reject) => {
resolve("2---成功啦");
});
// 该回调返回新的 promise,并且这个 promise 的执行器函数中调用 resolve("2---成功啦"), 此时该 promise 状态就变为成功,相应的 value 也会继续向下传递给 then 方法
// 在源码中会执行:p3.then((data) => {}, (err) => {})
return p3;
},
(err) => {
return "失败啦";
}
);
p2.then(
(data) => {
console.log(data); // 输出:'2---成功啦'
},
(err) => {
console.log(err);
}
);
手写实现
const resolvePromise = (promise2, x, resolve, reject) => {
if (promise2 === x) {
return reject(
new TypeError("我的报错: Chaining cycle detected for promise #<Promise")
);
}
+ // 判断 x 的实际类型, x 是 onFulfilled 或 onRejected 执行的结果
+ if ((x != null && typeof x === "object") || typeof x === "function") {
+ // 说明 x 是对象类型或者函数类型
+ try {
+ // 下面取 x.then 的时候可能会报错,所以这里需要外层需要使用 try...catch 捕获
+ let then = x.then;
+ if (typeof then === "function") {
+ // 有 then 方法,那么就认为 x 是一个 promise
+ // 注意:不要写成 x.then(), 必须写成 then.call(x), 因为需要修改 then() 中 this 指向 promise 实例
+ then.call(
+ x,
+ (y) => {
+ // 注意这里:有点不太好理解,因为外边已经调用了 resolve,那么 promise 状态就变为成功,我们内部帮用户调用then 方法,帮助用户继续把 value 继续传递下去
+ // console.log(y) // 此处的 y 就是 '2---成功啦'
+ // 这里的y就是外边新promise调用resolve传入的 '佩奇'
+ resolve(y);
+ },
+ (err) => {
+ reject(err);
+ }
+ );
+ } else {
+ // 不是promsie,那么就默认当作是普通值
+ resolve(x);
+ }
+ } catch (e) {
+ reject(e);
+ }
+ }
};
8. resolve 方法中传入 promise
基本使用
let { MyPromise } = require("./myPromise");
let p1 = new Promise((resolve, reject) => {
resolve("成功啦");
});
let p2 = p1.then(
(data) => {
return new Promise((resolve, reject) => {
// 下面的 resolve 方法中传入了一个 promise 实例
resolve(
new Promise((resolve, reject) => {
resolve(2000);
})
);
});
},
(err) => {
return "失败啦";
}
);
p2.then(
(data) => {
console.log("data:", data); // 输出: data: 2000
},
(err) => {
console.log("err:", err);
}
);
手写实现
- 这一步的实现我把它拆为如下几个步骤:
-
- 定义变量
-
- 使用递归
-
- 递归出口
-
- 最后补充
-
const resolvePromise = (promise2, x, resolve, reject) => {
// ...
+ // 1. 定义变量:用来标记是否调用过
+ let called;
if ((typeof x === "object" && x != null) || typeof x === "function") {
try {
let then = x.then;
if (typeof then === "function") {
then.call(
x,
(y) => {
+ // 3. 递归出口:为了防止调用了resolve后再调reject 或 调用了reject再调用resolve,事实上,只能调用其中一个
+ if (called) return;
+ called = true;
+ // 2. y 的值是一个 promise,使用递归:直到 resolve() 中传入的值为普通值
+ resolvePromise(promise2, y, resolve, reject);
},
(err) => {
+ // 3. 递归出口:同理
+ if (called) return;
+ called = true;
reject(err);
}
);
} else {
resolve(x);
}
} catch (err) {
+ // 3. 递归出口:同理
+ if (called) return;
+ called = true;
reject(err);
}
+ } else {
+ // 4. 最后补充:这里应该补充上去,是 return 的是普通值的时候,直接调用 promise2 的 resolve,
+ resolve(x);
+ }
};
简易的 Promise 完整代码
const PENDING = "PENDING";
const RESOLVE = "RESOLVE";
const REJECT = "REJECT";
const resolvePromise = (promise2, x, resolve, reject) => {
if (promise2 === x) {
return reject(
new TypeError("我的报错:Chaining cycle detected for promise #<Promise")
);
}
let called;
if ((x != null && typeof x === "object") || typeof x === "function") {
try {
let then = x.then;
if (typeof then === "function") {
then.call(
x,
(y) => {
if (called) return;
called = true;
resolvePromise(promise2, y, resolve, reject);
},
(err) => {
if (called) return;
called = true;
reject(y);
}
);
} else {
resolve(x);
}
} catch (err) {
if (called) return;
called = true;
reject(err);
}
} else {
resolve(x);
}
};
class MyPromise {
constructor(executor) {
this.status = PENDING;
this.value = undefined;
this.reason = undefined;
this.onResolvedCallbacks = [];
this.onRejectedCallbacks = [];
let resolve = (value) => {
if (this.status === PENDING) {
this.status = RESOLVE;
this.value = value;
this.onResolvedCallbacks.forEach((fn) => fn());
}
};
let reject = (reason) => {
if (this.status === PENDING) {
this.status = REJECT;
this.reason = reason;
this.onRejectedCallbacks.forEach((fn) => fn());
}
};
try {
executor(resolve, reject);
} catch (error) {
reject(error);
}
}
then(onFulfilled, onRejected) {
let promise2 = new MyPromise((resolve, reject) => {
if (this.status === RESOLVE) {
setTimeout(() => {
try {
let x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
}
if (this.status === REJECT) {
setTimeout(() => {
try {
let x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
}
if (this.status === PENDING) {
this.onResolvedCallbacks.push(() => {
setTimeout(() => {
try {
let x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
});
this.onRejectedCallbacks.push(() => {
setTimeout(() => {
try {
let x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
});
}
});
return promise2;
}
}
module.exports = {
MyPromise,
};