今天我们继续讲promise,本文主要包含对promise中resolve,reject和.then函数的介绍,最后通过对上述函数的理解手写出Promise源码。
Promise
当创建一个新的Promise实例时,构造函数会立即执行一个传递给它的executor函数,该函数接受两个参数:resolve和reject。这两个参数实际上是函数,用于改变Promise的状态。resolve用于在异步操作成功完成时调用,而reject则在异步操作失败时调用。
- 拥有三种状态为 pending fulfilled rejected
- 实例化一个 promise 会得到一个状态为 pending 的实例对象
- 该对象可以访问 promise 原型上的 then 方法
- 当该对象中的状态没有变更为 fulfilled,then 接收到的回调是不触发的
resolve
resolve函数的作用
resolve函数被用来解析一个Promise,使其从pending状态变为fulfilled(已解决)状态。
示例:
function a() {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('a is ok');
resolve()//如果我们不写resolve,.then里的逻辑是不生效的
}, 1000)
})
}
function b() {
console.log('b is ok');
}
a()
.then(
() => {
b()
})
如果不加
resolve(),
运行结果不会运行b():
当resolve被调用时,它还可以接受一个参数,这个参数会被传递给.then()方法中第一个回调函数作为其参数
new Promise((resolve, reject) => {
// 异步操作完成后
resolve(1); // 传递一个值1给resolve
});
.then(
(res) => {
console.log(res);
b()
})
.then()方法的使用
.then()方法允许你注册回调函数,这些函数会在Promise变为fulfilled状态时执行。如果你在resolve中传递了一个值,那么这个值会被作为.then()方法的第一个回调函数的参数。
例如:
new Promise((resolve, reject) => {
resolve(1); // 传递一个值1给resolve
})
.then((value) => {
console.log(value); // 输出1
});
如果没有调用resolve,Promise将保持在pending状态,因此任何.then()方法中的回调都不会被执行。
- 如果
resolve中带了值,那么.then()中的第一个回调函数将接收这个值作为参数。
.then()里的第二个回调完全等同于.catch()
在Promise的.then()方法中,可以传递两个回调函数:第一个是当Promise成功(即resolved)时调用的,第二个则是当Promise失败(即rejected)时调用的。
但是,从ES6开始,为了更清晰地区分成功和失败的处理逻辑,推荐使用.then()来处理成功的情况,以及单独的.catch()来处理失败的情况。
然而,在语法上,.then()的第二个参数确实可以用来处理rejected状态,这与.catch()是等效的。也就是说,下面两段代码是等价的:
使用.then()的第二个参数:
a()
.then(
(res) => {
console.log(res, 'res');
},
(err) => {
console.log(err, "err");
}
);
.catch():
a()
.then((res) => {
console.log(res, 'res');
})
.catch((err) => {
console.log(err, "err");
});
在两种情况下,如果Promise被reject,则err将会被打印。
但是,使用.catch()使得错误处理更加明显和易于理解,尤其是在链式调用中,它能够明确地表明这是处理错误的地方。
此外,.catch()还可以捕获前面.then()中抛出的异常,而.then()的第二个参数则不能。
例如:
a()
.then(() => {
throw new Error('An error occurred');
})
.catch((err) => {
console.log(err.message);
});
在这种情况下,.catch()将捕获在.then()内部抛出的异常。而如果使用.then()的第二个参数,它将无法捕获前面.then()中抛出的异常,除非你再次调用.then()并提供一个新的失败回调。
因此,尽管.then()的第二个参数在功能上与.catch()相似,但.catch()提供了更好的可读性和更全面的错误处理能力。
reject
调用reject函数时,会使Promise进入rejected(拒绝)状态。
一旦Promise处于rejected状态,任何随后的.then()方法将不会被调用,因为它们只处理fulfilled(已解决)的情况
我们可以使用.catch()方法来处理rejected状态。
.catch()实际上是一个简化的.then(null, onRejected)调用,它只关注rejected状态,并且会接收reject函数传入的任何值或错误作为参数。
function a() {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('a is ok');
reject(2) // 这里使用reject
}, 1000)
})
}
function b() {
console.log('b is ok');
}
a()
.then(
(res) => {
console.log(res);
b()
})
.catch(err => {
console.log(err); // 这里会捕获到reject抛出的值2
})
因为Promise通过reject(2)进入了rejected状态,所以.then()块中的代码不会执行,而.catch()中的代码会被调用来处理错误。
如果在Promise被拒绝后调用.then()而不调用.catch(),那么任何未处理的rejected状态都会导致程序中未捕获的异常,这通常不是我们想要的结果。
因此,确保在可能的rejected情况下使用.catch()来处理错误,或者在.then()之后链式调用.catch()来捕捉任何可能的异常。
总结:
- 必须调用
resolve或reject来改变Promise的状态。resolve用于标记异步操作的成功,使Promise变为fulfilled状态。reject用于标记异步操作的失败,使Promise变为rejected状态。.then()用于处理fulfilled状态,而.catch()用于处理rejected状态。
手写Promise
思路
- 创建Promise:当使用
new Promise()构造函数时,必须传递一个执行器函数executor,这个函数会被立即调用。- executor的参数:
executor函数接受两个参数:resolve和reject。这两个函数由Promise构造函数提供,用于改变Promise的状态。- 状态转换:
Promise有三种状态:pending(待定)、fulfilled(已解决)和rejected(已拒绝)。状态只能从pending变成fulfilled或rejected,且一旦状态改变,就不能再变回pending或改变为另一个状态。- 状态不变性:一旦
Promise变为fulfilled或rejected,其状态就永久固定,不会再改变。- then方法:每个
Promise都有then方法,它接受两个可选的回调函数:onFulfilled和onRejected,分别用于处理Promise成功和失败的情况。- then方法的执行:如果在调用
then时Promise已经处于fulfilled或rejected状态,那么相应的回调onFulfilled或onRejected将立即执行。如果Promise仍处于pending状态,那么这些回调将被存储起来,直到状态确定后才执行。- 可选的回调:
then方法的onFulfilled和onRejected参数是可选的,如果省略,then方法将假定一个默认的行为(对于onFulfilled,默认为返回值本身;对于onRejected,默认为抛出错误)。- 链式调用:
Promise的then方法可以被多次调用,每次调用都返回一个新的Promise,这使得你可以链接多个then方法来构建复杂的异步操作流程。- 传递结果:如果
then方法的回调返回一个值,这个值将作为下一个then方法中onFulfilled回调的参数。- 传递错误:如果
then方法的回调抛出一个异常,这个异常将作为下一个then方法中onRejected回调的参数。- 处理返回的Promise:如果
then方法的回调返回另一个Promise,那么整个链式调用会等待这个新Promise的结果。如果新Promise成功,链式调用继续向下执行;如果新Promise失败,链式调用会跳转到最近的onRejected回调。
完整代码:
class MyPromise {
constructor(executor) {
// 初始化状态和值
this.status = 'pending'; // 当前Promise的状态:pending, fulfilled, 或 rejected
this.value = null; // fulfilled时的值
this.reason = null; // rejected时的原因
this.onFulfilledCallbacks = []; // 存储所有成功回调
this.onRejectedCallbacks = []; // 存储所有失败回调
// 解析Promise,改变其状态到fulfilled
const resolve = value => {
if (this.status === 'pending') {
this.status = 'fulfilled';
this.value = value;
// 执行所有成功回调
this.onFulfilledCallbacks.forEach(fn => fn());
}
};
// 拒绝Promise,改变其状态到rejected
const reject = reason => {
if (this.status === 'pending') {
this.status = 'rejected';
this.reason = reason;
// 执行所有失败回调
this.onRejectedCallbacks.forEach(fn => fn());
}
};
// 尝试执行executor,如果抛出错误,自动调用reject
try {
executor(resolve, reject);
} catch (error) {
reject(error);
}
}
// then方法,用于链式调用和处理结果
then(onFulfilled, onRejected) {
// 默认处理,如果回调函数不存在,则直接返回值或抛出原因
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason; };
// 返回新的Promise,允许链式调用
return new MyPromise((resolve, reject) => {
// 根据当前状态,决定执行哪个回调
if (this.status === 'fulfilled') {
setTimeout(() => {
try {
// 调用onFulfilled并处理其结果
const x = onFulfilled(this.value);
this.resolvePromise(x, resolve, reject);
} catch (error) {
// 如果onFulfilled抛出错误,直接拒绝新的Promise
reject(error);
}
}, 0);
} else if (this.status === 'rejected') {
setTimeout(() => {
try {
// 调用onRejected并处理其结果
const x = onRejected(this.reason);
this.resolvePromise(x, resolve, reject);
} catch (error) {
// 如果onRejected抛出错误,直接拒绝新的Promise
reject(error);
}
}, 0);
} else {
// 如果状态是pending,将回调存储起来,等待状态改变
this.onFulfilledCallbacks.push(() => {
setTimeout(() => {
try {
const x = onFulfilled(this.value);
this.resolvePromise(x, resolve, reject);
} catch (error) {
reject(error);
}
}, 0);
});
this.onRejectedCallbacks.push(() => {
setTimeout(() => {
try {
const x = onRejected(this.reason);
this.resolvePromise(x, resolve, reject);
} catch (error) {
reject(error);
}
}, 0);
});
}
});
}
// 辅助函数,用于处理then方法中返回的值x
resolvePromise(promise2, x, resolve, reject) {
// 检查循环引用
if (promise2 === x) {
return reject(new TypeError('Chaining cycle'));
}
// 检查x的类型
if ((typeof x === 'object' && x !== null) || typeof x === 'function') {
let called = false; // 标记x.then是否已经被调用
try {
let then = x.then;
if (typeof then === 'function') {
// 如果x是一个Promise,调用其then方法
then.call(x, y => {
if (called) return;
called = true;
this.resolvePromise(promise2, y, resolve, reject);
}, r => {
if (called) return;
called = true;
reject(r);
});
} else {
// 如果x不是一个Promise,直接解析
if (called) return;
called = true;
resolve(x);
}
} catch (error) {
// 捕获x.then抛出的错误
if (called) return;
called = true;
reject(error);
}
} else {
// 如果x不是对象或函数,直接解析
resolve(x);
}
}
}
module.exports = MyPromise;
以上就是本文的全部内容,通过手写Promise,我们不仅加深了对Promise内部机制的理解,还掌握了如何实现自己的Promise库,这对于进一步学习异步编程和深入JavaScript核心概念有着重要的意义。希望本文对你有所帮助,感谢你的阅读!
Alo365 
![[爱了]](/js/img/d1.gif)
![[尴尬]](/js/img/d16.gif)