Promise 是现代 JavaScript 开发中最常用的异步编程方案之一,它解决了回调地狱的问题,让异步代码更优雅地表达。然而,在使用 Promise 的过程中,我们可能会遇到一些问题,其中最常见的问题就是重复调用 resolve 和 reject 方法。
问题根源
在使用 Promise 时,我们常常需要手动编写 resolve 和 reject 函数。resolve 函数用于完成 Promise,把一个异步操作回传的结果作为参数传递给后续的 then 方法;而 reject 函数则用于拒绝 Promise,把一个异步操作回传的错误作为参数传递给后续的 catch 方法。例如:
-- -------------------- ---- ------- ----- ------- - --- ----------------- ------- -- - -- ---- ------------- -- - ----- ------ - ------------- - --- - --------- - --- ---------------- ---- -------- -- ------- ---------- ------ - --------------- - ---- - ---------------- - -- ------ --- ------------------- -- - ---------------- ------- ------------ -------------- -- - ------------------------ ---- ------ ------------------- ---
上面的代码中,我们用 Promise 封装了一个异步操作,在异步操作完成后,根据操作结果调用 resolve 或 reject 方法。在 Promise 完成后,我们用 then 方法获取操作结果,或通过 catch 方法获取错误信息。
然而,出于某些原因,我们可能会在 Promise 完成后再次调用 resolve 或 reject 方法,例如:
promise.then(result => { console.log(`Got result: ${result}`); promise.resolve('done'); // 重复调用 resolve 方法 }).catch(error => { console.error(`Something went wrong: ${error.message}`); });
这样做的结果就是,即使我们在后面调用了 resolve 方法,Promise 的状态仍然是已完成(fulfilled),并且按照第一次调用 resolve 方法的参数执行后续的 then 方法。这是因为 Promise 的状态是不可逆的,一旦状态被确定,就不能再次改变。因此,重复调用 resolve 和 reject 方法不仅是无效的,而且会引发一些难以预料的问题。
解决方案
如何解决 Promise 中重复调用 resolve 和 reject 方法引发的问题呢?一种常见的解决方案是使用 Promise 内部的状态机,避免在状态发生变化后继续调用 resolve 和 reject 方法。
Promise 的状态机有三个状态:待定(pending)、已完成(fulfilled)和已拒绝(rejected)。Promise 的状态只能从待定转变为已完成或已拒绝,而且一旦转变为已完成或已拒绝,就不能再次改变。因此,我们可以在 Promise 内部添加状态标识,用于判断 Promise 的状态是否已经发生变化。例如:
-- -------------------- ---- ------- ----- --------- - --------------------- - ----------- - ---------- ---------- - ---------- ----------- - ---------- ------------------------- - --- ------------------------ - --- ----- ------- - ----- -- - -- ------------ --- ---------- - ----------- - ------------ ---------- - ------ ------------------------------------------ -- ----------------- - - ----- ------ - ------ -- - -- ------------ --- ---------- - ----------- - ----------- ----------- - ------- ----------------------------------------- -- ------------------ - - --- - ----------------- -------- - ------------ - -------------- - - ----------------- ----------- - ----------- - ------ ----------- --- ---------- - ----------- - ----- -- ------ ---------- - ------ ---------- --- ---------- - ---------- - ------ -- ------ -------- ----- -------- - --- ------------------- ------- -- - -- ------------ --- ------------ - ------------- -- - --- - ----- - - ------------------------ ----------------------------- -- -------- -------- -- -- -------- - - --- - ------------ - -------------- - -- --- - ---- -- ------------ --- ----------- - ------------- -- - --- - ----- - - ------------------------ ----------------------------- -- -------- -------- -- -- -------- - - --- - ------------ - -------------- - -- --- - ---- - ------------------------------------ -- - ------------- -- - --- - ----- - - ------------------- ----------------------------- -- -------- -------- -- -- -------- - - --- - ------------ - -------------- - -- --- --- ------------------------------------ -- - ------------- -- - --- - ----- - - ------------------- ----------------------------- -- -------- -------- -- -- -------- - - --- - ------------ - -------------- - -- --- --- - --- ------ --------- - ----------------- - ------ --------------- ------------ - ------------------------ -- -------- ------- - -- --------- --- -- - ------ ---------- ------------------- ----- -------- --- ----------- - -- -- ---------- ---------- - ------------ -- - ----------------------------- ------ -------- -------- -- ---- -- ------ -- - --------------- --- ------- - -- -- --- ---- -- ------- - --- -------- -- ------ - --- ------------ - --- ----- --- - ---- - ------- - ------------ - -------------- - -- ------- ---- --- ----------- - --- ------ - ------ --- - ------------ ----- -- - -- -------- ------- ------ - ----- ----------------------------- ------ -------- -------- -- ------ -- - -- -------- ------- ------ - ----- --------------- --- - ------------ - -- -------- ------- ------ - ----- -------------- - ------- - - ----------- - ------ -------------- - -- ------ ---------- ---------- ------ ------ ------ --- ----------------- -- ---------------- - ------ -------------- - ------ --- ------------------- ------- -- ---------------- - ------ ------------- - ------ --- ------------------- ------- -- - --- ----- - -- ----- ------ - --- ----- ------- - ------- ------ -- - ------------- - ------ -------- -- ------ --- ---------------- - ---------------- - - -------------------------- ------ -- - ------------------------------------- -- - -------------- ------- -- ------ -- - --------------- --- --- --- - ------ -------------- - ------ --- ------------------- ------- -- - ------------------------ -- - ------------------------------------- -- - --------------- -- ------ -- - --------------- --- --- --- - -
在上面的代码中,我们对 Promise 进行了一些改良,添加了状态标识、完成后执行的回调函数列表等特性,并在内部添加了 resolvePromise 方法。resolvePromise 方法用于解析通过 then 方法返回的新的 Promise 对象和其值的关系,并根据情况调用外部的 resolve 或 reject 方法。
这样一来,当我们在状态发生变化后调用 resolve 和 reject 方法时,Promise 就会根据状态标识,判断是否需要继续执行 resolve 和 reject 方法。如果状态已经发生变化,那么再次调用 resolve 和 reject 方法就不会再次执行所有的回调函数。
promise.then(result => { console.log(`Got result: ${result}`); promise.resolve('done'); // 重复调用 resolve 方法,但不会造成问题 }).catch(error => { console.error(`Something went wrong: ${error.message}`); });
现在,我们已经成功地解决了 Promise 中重复调用 resolve 和 reject 方法引发的问题。我们可以安心地使用 Promise,写出更加稳健和优雅的异步代码。
总结
本文介绍了 Promise 中重复调用 resolve 和 reject 方法引发的问题,并给出了一种解决方案。通过添加状态标识和状态变化的判断,可以避免在状态发生变化后继续调用 resolve 和 reject 方法,让 Promise 的表现更加可靠和优雅。
最后,我们提醒读者,虽然本文已经尽可能详细地解释了 Promise 中的问题和解决方案,但在实际编码中,还需要多加练习和思考,才能写出更好的、更健壮的异步代码。
来源:JavaScript中文网 ,转载请注明来源 https://www.javascriptcn.com/post/64b27e2f48841e9894eb17a9