Promise 是 JavaScript 中处理异步编程的一种重要方式。相较于传统的回调函数,Promise 更为简洁、易于扩展和维护。但是在实际使用中,存在一些常见的误区,特别是对于初学者而言,需要引起重视。本文将介绍 Promise 使用中常见的误区,并提供相应的指导和示例代码。
一、对 Promise 链式调用的理解误区
在 Promise 官方文档中,我们看到了许多关于链式调用的使用方法:
job1().then(job2).then(job3).catch(errorHandler);
这里的 job1
、job2
、job3
函数实现了异步逻辑的处理,同时返回一个 Promise 对象,然后通过 .then()
方法进行链式调用(可以继续使用 .catch() 处理错误)。关于 Promise 的链式调用还包括:
- 单个
.then()
子句可以连接多个回调函数,但每个回调函数必须返回 Promise 对象。 .then()
子句本身也返回 Promise 对象,可以继续进行链式调用。.then()
子句中的函数可以直接返回一个值,该值将用 Promise 对象作为参数执行。
然而,在实际使用过程中,可能产生以下的误区:
1. 不返回 Promise 对象
在链式调用的使用中,每个回调函数必须返回一个 Promise 对象,否则会中断整个链式调用。例如:
-- -------------------- ---- ------- -------- ----- - ------ --- ----------------- ------- -- - ------------- -- - ---------------- ----------- ---------- -- ------ --- - ------------- -- - ----------------- ----------- ---------------- -- - ----------------- ----------- -------------- -- - ------------------- ---
此时我们希望顺序输出:job finished
、job1 finished
、job2 finished
。然而,由于第二个 .then()
子句中的回调函数没有返回一个 Promise 对象,导致第三个 .then()
子句不会被执行。正确的做法是在 job2
上返回一个 Promise:
job().then(() => { console.log('job1 finished'); return 'job1 data'; }).then((result) => { console.log('job2 finished, received:', result); }).catch((err) => { console.error(err); });
2. 不用错误处理函数
Promise 在处理过程中,可以使用 .catch()
方法来捕获异常并进行处理。正确的写法是在链式调用的最后一处增加 .catch()
:
job().then(() => { console.log('job1 finished'); return 'job1 data'; }).then((result) => { console.log('job2 finished, received:', result); }).catch((err) => { console.error(err); });
注意,如果在链式调用的每个子句中都使用了 .catch()
子句,实际上是没有必要的。在该子句之后的所有回调函数都会得到相应的错误参数。
3. 滥用 .then()
语法
.then()
关键字的作用是将程序在异步处理之后的下一步操作“挂起”,等待异步调用的结果出现后再继续进行。但是在使用 .then()
的过程中,一些开发者容易出现链式调用过于复杂或存在过多嵌套等问题,导致代码难以维护。应当合理运用 Promise 的应用场景和语法特点,保证代码的可读性和可维护性。
二、Promise.all 的使用误区
Promise.all()
是将多个 Promise 对象打包成一个 Promise 对象,全部处理完成后才触发后续操作,例如:
Promise.all([promise1, promise2, promise3]) .then((results) => { console.log('all jobs finished'); }) .catch((error) => { console.error(error); });
在使用 Promise.all()
的过程中,可能存在以下误区:
1. Promise.all 参数是空数组
如果传递给 Promise.all()
函数的参数是空数组,则会立即返回一个 resolved Promise 对象。这是因为这个 Promise 不包含任何等待的异步任务,所以不需要等待任何异步操作完成:
Promise.all([]).then(() => { console.log('Array is empty'); });
对于这个问题,实际上可以直接使用 Promise 对象作为参数:
-- -------------------- ---- ------- -------- -------------- - ------ --- ----------------- -- - ---------- --- - ------------------------------------- -- - ------------------ -- --- -------- ---
2. Promise.all 中有 rejected Promise
如果 Promise.all()
中的任何一个 Promise 对象出现无法解决的问题,则整个方法将直接返回一个 rejected Promise 对象。这时,我们需要在 .catch()
部分获取错误信息:
Promise.all([promise1, promise2, promise3]) .then((results) => { console.log('all jobs finished'); }) .catch((error) => { console.error(error); });
3. Promise.all 中的 Promise 只要有一个完成就会返回
Promise.all()
的最终结果是由所有 promise 完成时产生的结果组成的数组。如果其中的任何一个 promise 被拒绝,那么调用 Promise.all()
的 promise 将被拒绝。因此,如果您有多个异步任务,其中一个完成时就可以处理结果,而其他不必等待,则不要使用Promise.all()
函数。
三、使用 async/await 时的误区
async 和 await 是 ES7/ES2017 标准中用于处理异步编程的新特性,在使用时也存在一些错误的使用方式:
1. 非异步操作调用 async/await
使用 await
关键字需要在异步函数中。否则,我们就会收到下面的错误:
const getData = async () => { const url = 'https://jsonplaceholder.typicode.com/todos/1'; const response = await fetch(url); return await response.json(); }; const jsonData = await getData(); // Uncaught SyntaxError: await is only valid in an async function console.log(jsonData);
正确的做法是在异步函数中调用 await
:
const displayData = async () => { const jsonData = await getData(); console.log(jsonData); }; displayData();
2. 忘了处理错误
使用 async/await
的代码段,需要使用 try-catch 语句来捕捉和处理错误。否则,异步调用的错误可能会直接结束程序的执行:
-- -------------------- ---- ------- ----- ------- - ----- -- -- - ----- --- - -------------------- ----- -------- - ----- ----------- ------ ----- ---------------- -- --- - ----- -------- - ----- ---------- ---------------------- - ----- ----- - ------------------- -
3. 不正确地编写异步函数
异步函数应该返回一个 Promise。如果它们没有返回一个 Promise,则执行将不会像预期一样正常工作:
-- -------------------- ---- ------- ----- ------- - ----- -- -- - ----- --- - ----------------------------------------------- ----- -------- - ----- ----------- ------ ----- ---------------- -- -- ---- ------- ----- ----- ----------- - ----- -- -- - ------ ---------- -- ----------------------- -- ------------------- -- -------- --- -------- ---------- --------------------- -- --- - --------
正确的做法是在异步函数中使用 Promise 返回:
-- -------------------- ---- ------- ----- ------- - ----- -- -- - ----- --- - ----------------------------------------------- ----- -------- - ----- ----------- ------ ----- ---------------- -- ----- ----------- - ----- -- -- - ------ --------------------------- -- ----------------------- -- -------------------
结论
Promise 是一种强大的异步编程方案,但在使用时需注意一些常见的错误。本文介绍了 Promise 中常见的误区,包括 Promise 链式调用的理解问题、Promise.all 的使用问题,以及 async/await 时的误区。正确地使用 Promise 不仅可以提供高效的异步处理能力,还可以提升代码的可维护性和可读性。
来源:JavaScript中文网 ,转载请注明来源 https://www.javascriptcn.com/post/6700f5980bef792019aee974