Promise
promise 的参数和状态
Promise 是一个构造函数,参数是一个可执行函数,默认会同步执行 可执行函数有两个参数 resolve, reject。promise 有三个状态 pending, fulfill, rejected.
promise 实例有个 then 方法, then 有两个函数参数, 如果成功则执行成功的函数并传入成功的 value, 如果失败则执行失败的函数并将失败的 reason 传入。
const PENDING = "PENDING";
const FULFILLED = "FULFILLED";
const REJECTED = "REJECTED";
class Promise {
constructor(executor) {
this.status = PENDING;
// 用户调用 resolve 和 reject 可以将对应的结果暴露在当前的 promise 实例上
this.value = undefined; // 实例上需要使用
this.reason = undefined;
const resolve = (value) => {
this.value = value;
this.status = FULFILLED;
};
const reject = (reason) => {
this.reason = reason;
this.status = REJECTED;
};
executor(resolve, reject); // 默认 new Promise 中的函数会立即执行
}
then(onFulfilled, onRejected) {
if (this.status === FULFILLED) {
onFulfilled(this.value);
}
if (this.status === REJECTED) {
onRejected(this.reason);
}
}
}
try catch peomise 的执行函数
executor 执行的过程可能会报错,需要 try catch 一下,报错后会走 reject 函数
try {
executor(resolve, reject); // 默认 new Promise 中的函数会立即执行
} catch (error) {
reject(error);
}
promise 状态切换
-
如果用户调了 resolve, promise 的 status 会变成 fulfilled, 并且会把成功的 value 保存起来
-
如果用户调了 reject, promise 的 status 会变成 rejected, 并且会把失败的 reason 保存起来
-
promise 的状态只能从 pending 变成 fulfilled 或 rejected, 不能从 fulfilled 变成 rejected 或 从 rejected 变成 fulfilled
const resolve = (value) => {
if (this.status === PENDING) {
// // 不能 从失败变成成功,只能从 pending 变成成功
this.value = value;
this.status = FULFILLED;
}
};
const reject = (reason) => {
console.log(this.status === PENDING);
if (this.status === PENDING) {
// 不能从成功变成失败,只能从 pending 变成失败
this.reason = reason;
this.status = REJECTED;
}
};
异步执行 resolve 和 reject
如果 resolve 或 reject 是在异步中执行的, 执行 then 方法的时候 promise status 是 pending 就不会执行成功或失败的回调了。需要在执行 then 的时候先存起来,等执行 resolve 或 reject 的时候再执行。(发布订阅模式)
异步执行 resolve 或 reject
new Promise((resolve, reject) => {
setTimeout(() => {
resolve(1)
})
})
此时 then 方法执行时,还没有执行 resolve, 如果不做处理后面 resolve 执行就拿不到返回值了。
const PENDING = "PENDING";
const FULFILLED = "FULFILLED";
const REJECTED = "REJECTED";
class Promise {
constructor(executor) {
this.status = PENDING;
this.value = undefined; // 实例上需要使用
this.reason = undefined;
this.onResolvedCallbacks = [];
this.onRejectedCallbacks = [];
const resolve = (value) => {
if (this.status === PENDING) {
// // 不能 从失败变成成功,只能从 pending 变成成功
this.value = value;
this.status = FULFILLED;
this.onResolvedCallbacks.forEach((fn) => fn());
}
};
const reject = (reason) => {
console.log(this.status === PENDING);
if (this.status === PENDING) {
// 不能从成功变成失败,只能从 pending 变成失败
this.reason = reason;
this.status = REJECTED;
this.onRejectedCallbacks.forEach((fn) => fn());
}
};
try {
executor(resolve, reject); // 默认 new Promise 中的函数会立即执行
} catch (error) {
reject(error);
}
}
then(onFulfilled, onRejected) {
if (this.status === FULFILLED) {
onFulfilled(this.value);
}
if (this.status === REJECTED) {
onRejected(this.reason);
}
if (this.status === PENDING) {
// 先把 then的回调存起来, 发布订阅模式
console.log("PENDING");
this.onResolvedCallbacks.push(() => {
onFulfilled(this.value);
});
this.onRejectedCallbacks.push(() => {
onRejected(this.reason);
});
}
}
}
then 方法可以链式调用
可以在 then 方法(成功和失败)中返回一个 promise, promise 会采用返回的 promise 的成功的值或失败的原因, 传递到外层下一层 then 中.
- then 方法中成功的回调或者失败的回调
返回的是一个 promise, 那么会采用返回的 promise 的状态, 走外层下一次 then 中的成功或失败, 同时将 promise 处理后的结果向下传递 - then 方法中成功的回调或者失败的回调
返回的是一个普通值(不是 promise), 那么会将返回的结果传递到下一次 then 的成功回调中去 - then 方法中成功的回调或者失败的回调
执行时出错或返回失败的 promise会走到外层下一个 then 中的失败中去
readFile("./a.txt", "utf8")
.then(
(data) => {
return readFile(data, "utf8");
},
(err) => {
console.log("fail", err);
}
)
.then(
() => {
console.log(data);
},
(err) => {
console.log("fail2", err);
}
);
实现 promise 的链式调用
如果是通过 return this 来实现,是不行的, 因为 promise 的状态只能从 pending 变成 fulfilled 或 rejected。 return this 的话, 后面的 then 方法的状态没办法改变了。
通过 return new Promise() 每次产生一个全新的 promise, 来保证状态可以正常的切换。
then(onFulfilled, onRejected) {
// 每次调用 then 方法, 都返回一个全新的 promise
const promise2 = new Promise((resolve, reject) => {
// x 就是上一个 then 成功或失败的返回值, 这个 x 决定 promise2 走成功还是走失败
if(this.status === FULFILLED) {
try {
const x = onFulfilled(this.value)
resolve(x)
} catch (error) {
reject(error)
}
}
if(this.status === REJECTED) {
try {
const x = onRejected(this.reason)
resolve(x)
} catch (error) {
reject(error)
}
}
if (this.status === PENDING) { // 先把 then的回调存起来, 发布订阅模式
// console.log('PENDING');
this.onResolvedCallbacks.push(() => {
try {
const x = onFulfilled(this.value)
resolve(x)
} catch (error) {
reject(error)
}
})
this.onRejectedCallbacks.push(() => {
try {
const x = onRejected(this.reason)
resolve(x)
} catch (error) {
reject(error)
}
})
}
})
return promise2
}
x 的值可能又是一个 promise, 需要判断 x 的值再进一步处理。通过 resolvePromise 统一处理
resolvePromise(x, promise2, resolve, reject) 替换掉 resolve(x);
这里用到的了 promise2, 但是 promise2 是通过 new Promise 的过程中获取的。直接这么用的话就报错了。我们可以在下一个事件循环中使用,这里使用了 queueMicrotask
queueMicrotask(() => {
try {
const x = onRejected(this.reason)
resolvePromise(x, promise2, resolve, reject)
} catch (error) {
reject(error)
}
})
resolvePromise 的实现
resolvePromise 的功能主要是判断 x 的值及做出相应的处理
function resolveromise(x, promise2, resolve, reject) {
if (x === promise2) {
// 问题1
return reject(new Error("循环引用"));
}
// 继续判断 x 是不是一个 promise, promise 需要有 then 方法(啥时候是函数, 别人写的 promise 就有可能是函数)
if ((typeof x === "object" && x !== null) || typeof x === "function") {
// 这种情况下才有可能是 promise
// 继续判断 x 是否有 then
// let then = x.then
let called = false; // 问题3
try {
let then = x.then; // 尝试取 then 的方法 {then: 1} // 这种情况 then 不是一个方法
if (typeof then === "function") {
// x.then// x.then 是一个函数, 就认为 x 是一个 promise 了
// x.then() // 这个会再次取一次属性,触发 get 方法
// then.call(x) // 这个不会
// 问题2
then.call(
x,
(y) => {
// y 可能还是一个 promise, 所以要再次进行解析流程
if (called) return; // 防止多次调用成功或失败
called = true;
resolvePromise(y, promise2, resolve, reject); // 这里的 promise2 应该是 x 吧
// resolve(y)
},
(r) => {
if (called) return; // 防止多次调用成功或失败
called = true;
reject(r);
}
);
} else {
// x = {then: 1}
resolve(x);
}
} catch (error) {
if (called) return; // 防止多次调用成功或失败
called = true;
reject(error); // 让 promise2 变成失败态
}
} else {
// 如果不是 promise, fulfill the promise with x
// x 是普通值
resolve(x);
}
}
问题 1
const promise2 = new Promise((resolve, reject) => {
resolve();
}).then((data) => {
return promise2;
});
问题 2
避免多次取值报错
let time = 0;
Object.defineProperty({}, "then", {
get() {
if (time++ === 2) {
return new Error("error");
}
},
});
问题3
多次调用 resolve 或 reject, 只有第一次生效
new Promise((resolve, reject) => {
resolve(1)
resolve(2)
})
then 透传值
new Promise((resolve, reject)=> {
resolve(1)
}).then().then().then(data => {
console.log(data) // 1
})
当 then 方法调用不传参数时,接收到的值是可以透传到下一个 then 的。 需要为 then 的参数添加一个默认值兜底。
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : v => v
onRejected = typeof onRejected === 'function' ? onRejected : e => { throw e}