调试 JavaScript 中的异步代码有时就像在雷区中穿梭。你不知道 console.log 会在何时何地打印出来,也不知道代码是如何执行的。
你很难正确构造异步代码,使其按照你的意图以正确的顺序执行。
如果在编写异步代码时能得到一些指导,并在即将出错时收到一条有用的信息,那岂不更好?
幸运的是,在将错误推向生产环境之前,我们有一些规则来捕捉这些错误。以下是一份经过编译的linting规则列表,可为你在 JavaScript 和 Node.js 中编写异步代码提供具体帮助。
即使你最终没有在项目中使用这些规则,阅读它们的说明也会让你更好地理解异步代码,并提高你的开发技能。
ESLint异步代码规则
ESLint 默认提供以下规则。将它们添加到 .eslintrc
配置文件中即可启用。
no-async-promise-executor
该规则不允许将async
函数传递给new Promise
构造函数。
// ❌
new Promise(async (resolve, reject) => {});
// ✅
new Promise((resolve, reject) => {});
虽然从技术上讲,向 Promise 构造函数传递异步函数是有效的,但出于以下两个原因,这样做通常是错误的。首先,如果异步函数抛出错误,错误将丢失,不会被新构造的 Promise 拒绝。其次,如果在构造函数内部使用了 await
,那么外层的 Promise 可能就没有必要了,可以将其删除。
no-await-in-loop
该规则不允许在循环内使用await
。
在对可迭代对象的每个元素进行操作并等待异步任务时,往往表明程序没有充分利用 JavaScript 的事件驱动架构。通过并行执行任务,可以大大提高代码的效率。
// ❌
for (const url of urls) {
const response = await fetch(url);
}
// ✅
const responses = [];
for (const url of urls) {
const response = fetch(url);
responses.push(response);
}
await Promise.all(responses);
如果你想按顺序运行任务,我建议你使用行内注释暂时禁用该规则:// eslint-disable-line no-await-in-loop
。
no-promise-executor-return
该规则不允许在 Promise 构造函数中返回值。
// ❌
new Promise((resolve, reject) => {
return result;
});
// ✅
new Promise((resolve, reject) => {
resolve(result);
});
在 Promise 构造函数中返回的值不能使用,也不会对 promise 产生任何影响。应将该值传递给resolve
,如果发生错误,则调用 reject
并告知错误信息。
该规则不会阻止你在 Promise 构造函数中的嵌套回调内返回值。请务必使用
resolve
或reject
来结束promise。
require-atomic-updates
该规则不允许将赋值与 await
结合使用,否则会导致竞赛条件。
请看下面的示例,你认为 totalPosts
的最终值会是多少?
// ❌
let totalPosts = 0;
async function getPosts(userId) {
const users = [{ id: 1, posts: 5 }, { id: 2, posts: 3 }];
await sleep(Math.random() * 1000);
return users.find((user) => user.id === userId).posts;
}
async function addPosts(userId) {
totalPosts += await getPosts(userId);
}
await Promise.all([addPosts(1), addPosts(2)]);
console.log('Post count:', totalPosts);
也许你已经感觉到这是一个骗人的问题,答案不是 8。没错,totalPosts
打印的是 5 或 3。自己在浏览器中试试吧。
问题在于读取和更新 totalPosts
之间存在时间差。这就造成了一个竞赛条件,当值在单独的函数调用中更新时,更新不会反映在当前函数的作用域中。因此,这两个函数都将其结果添加到 totalPosts
的初始值 0 中。
要避免这种竞赛条件,应确保在更新变量的同时读取变量。
// ✅
let totalPosts = 0;
async function getPosts(userId) {
const users = [{ id: 1, posts: 5 }, { id: 2, posts: 3 }];
await sleep(Math.random() * 1000);
return users.find((user) => user.id === userId).posts;
}
async function addPosts(userId) {
const posts = await getPosts(userId);
totalPosts += posts; // variable is read and immediately updated
}
await Promise.all([addPosts(1), addPosts(2)]);
console.log('Post count:', totalPosts);
max-nested-callbacks
该规则强制限制回调的最大嵌套深度。换句话说,该规则可防止回调地狱!
/* eslint max-nested-callbacks: ["error", 3] */
// ❌
async1((err, result1) => {
async2(result1, (err, result2) => {
async3(result2, (err, result3) => {
async4(result3, (err, result4) => {
console.log(result4);
});
});
});
});
// ✅
const result1 = await asyncPromise1();
const result2 = await asyncPromise2(result1);
const result3 = await asyncPromise3(result2);
const result4 = await asyncPromise4(result3);
console.log(result4);
深度嵌套会使代码难以阅读,更难以维护。在编写 JavaScript 异步代码时,将回调重构为promise,并使用现代的 async/await
语法。
no-return-await
该规则不允许不必要的return await
。
// ❌
async () => {
return await getUser(userId);
}
// ✅
async () => {
return getUser(userId);
}
由于async
函数返回的所有值都已封装在 promise 中,因此等待 promise 并立即返回是不必要的。因此,你可以直接返回 promise。
当周围有 try...catch
语句时,这条规则会出现例外。移除 await
关键字会导致不捕获拒绝的promise。在这种情况下,我建议你将结果赋值给另一行的变量,以明确意图。
//
标签:异步,const,规则,await,Promise,ESLint,编写,async,eslint
From: https://www.cnblogs.com/chuckQu/p/17978582