技巧篇-ESLint异步-《前端知识进阶》

admin 2025-11-01 15:27:33 编程 来源:ZONE.CI 全球网 0 阅读模式
  • 异步代码的ESLint规则
    • 1. no-async-promise-executor
    • 2. no-await-in-loop
    • 3. no-promise-executor-return
    • 4. require-atomic-updates
    • 5. max-nested-callbacks
    • 6. no-return-await
    • 7. prefer-promise-reject-errors
  • Node.js 特定规则
    • 8. node/handle-callback-err
    • 9. node/no-callback-literal
    • 10. node/no-sync
  • TypeScript 特定规则
    • 11. @typescript-eslint/await-thenable
    • 12. @typescript-eslint/no-floating-promises
    • 13. @typescript-eslint/no-misused-promises
    • 14. @typescript-eslint/promise-function-async

    在 JavaScript 编写调试异步代码时,ESlint 可以帮我们及时发现一些错误。即使没有在项目中用到这些规则,理解它们的含义也将有助于帮助我们更好地理解和编写异步代码。

    异步代码的ESLint规则

    以下规则默认随ESLint一起提供,可以在 .eslintrc 配置文件中启用它们。

    1. no-async-promise-executor

    此规则不允许将async函数传递给new Promise构造函数。

    1. // ❌
    2. new Promise(async (resolve, reject) => {});
    3. // ✅
    4. new Promise((resolve, reject) => {});

    虽然将异步函数传递给 Promise 构造函数在技术上是没有问题的,但由于以下两个原因,这样做通常都是错误的:

    • 如果异步函数抛出错误,错误将会丢失而不会被新构造的 Promise 拒绝;
    • 如果 await 在构造函数内部使用,包装 Promise 是不必要的,可以将其删除。

      2. no-await-in-loop

      此规则不允许在循环内使用 await。

    当对可迭代的每个元素执行操作并等待异步任务时,通常表明程序没有充分利用JavaScript的事件驱动架构。通过并行执行这些任务,可以大大提高代码的效率。

    1. // ❌
    2. for (const url of urls) {
    3. const response = await fetch(url);
    4. }
    5. // ✅
    6. const responses = [];
    7. for (const url of urls) {
    8. const response = fetch(url);
    9. responses.push(response);
    10. }
    11. await Promise.all(responses);

    当确实需要按照顺序执行任务的情况下,建议使用内联注释禁用此规则:

    1. // eslint-disable-line no-await-in-loop。

    3. no-promise-executor-return

    此规则不允许在 Promise 构造函数中返回值。

    1. // ❌
    2. new Promise((resolve, reject) => {
    3. return result;
    4. });
    5. // ✅
    6. new Promise((resolve, reject) => {
    7. resolve(result);
    8. });

    在 Promise 构造函数中返回的值不能被使用并且不会以任何方式影响 Promise。 应该将值传递给 resolve,如果发生错误,则调用 reject 并显示错误。

    此规则不会阻止在 Promise 构造函数的嵌套回调中返回值。 始终确保使用 resolve 或 reject 来完成 Promise。

    4. require-atomic-updates

    此规则不允许由于 await 或 yield 的使用而可能导致出现竞态条件的赋值。

    来看下面的例子,totalPosts 的最终值是多少?

    1. // ❌
    2. let totalPosts = 0;
    3. async function getPosts(userId) {
    4. const users = [{ id: 1, posts: 5 }, { id: 2, posts: 3 }];
    5. await sleep(Math.random() * 1000);
    6. return users.find((user) => user.id === userId).posts;
    7. }
    8. async function addPosts(userId) {
    9. totalPosts += await getPosts(userId);
    10. }
    11. await Promise.all([addPosts(1), addPosts(2)]);
    12. console.log('Post count:', totalPosts);

    这里 totalPosts 不会打印 8,而会打印 5 和 3。问题就在于读取和更新 totalPosts 之间存在时间间隔。 这会导致竞态条件,使得在单独的函数调用中更新值时,更新不会反映在当前函数范围中。 因此,这两个函数都将它们的结果添加到初始值为 0 的 totalPosts 中。

    为了避免这种竞态条件,应该确保在更新变量的同时读取变量:

    1. // ✅
    2. let totalPosts = 0;
    3. async function getPosts(userId) {
    4. const users = [{ id: 1, posts: 5 }, { id: 2, posts: 3 }];
    5. await sleep(Math.random() * 1000);
    6. return users.find((user) => user.id === userId).posts;
    7. }
    8. async function addPosts(userId) {
    9. const posts = await getPosts(userId);
    10. totalPosts += posts; // variable is read and immediately updated
    11. }
    12. await Promise.all([addPosts(1), addPosts(2)]);
    13. console.log('Post count:', totalPosts);

    5. max-nested-callbacks

    此规则会强制回调的最大嵌套深度。 换句话说,这条规则可以防止回调地狱。

    1. /* eslint max-nested-callbacks: ["error", 3] */
    2. // ❌
    3. async1((err, result1) => {
    4. async2(result1, (err, result2) => {
    5. async3(result2, (err, result3) => {
    6. async4(result3, (err, result4) => {
    7. console.log(result4);
    8. });
    9. });
    10. });
    11. });
    12. // ✅
    13. const result1 = await asyncPromise1();
    14. const result2 = await asyncPromise2(result1);
    15. const result3 = await asyncPromise3(result2);
    16. const result4 = await asyncPromise4(result3);
    17. console.log(result4);

    深层的嵌套使代码难以阅读和维护。 在编写JavaScript 异步代码时,可以将回调重构为 Promise 并使用 async/await 语法。

    6. no-return-await

    此规则不允许返回不必要的 await。

    1. // ❌
    2. async () => {
    3. return await getUser(userId);
    4. }
    5. // ✅
    6. async () => {
    7. return getUser(userId);
    8. }

    等待一个 Promise 并立即返回它是没有必要的,因为从异步函数返回的所有值都包含在一个 Promise 中。 因此,可以直接返回 Promise。

    此规则的一个例外情况就是当有 try…catch 语句时, 删除 await 关键字将导致不会捕获 Promise 拒绝的原因。 在这种情况下,建议将结果分配给变量以明确意图。

    1. // 👎
    2. async () => {
    3. try {
    4. return await getUser(userId);
    5. } catch (error) {
    6. // 处理错误
    7. }
    8. }
    9. // 👍
    10. async () => {
    11. try {
    12. const user = await getUser(userId);
    13. return user;
    14. } catch (error) {
    15. // 处理错误
    16. }
    17. }

    7. prefer-promise-reject-errors

    此规则在拒绝 Promise 时强制使用 Error 对象。

    1. // ❌
    2. Promise.reject('An error occurred');
    3. // ✅
    4. Promise.reject(new Error('An error occurred'));

    最好的做法就是始终拒绝带有 Error 对象的 Promise。 这样做将更容易跟踪错误的来源,因为 Error 对象存储在堆栈跟踪中。

    Node.js 特定规则

    以下规则是 eslint-plugin-node 插件为 Node.js 提供的附加 ESLint 规则。 要使用它们,需要安装插件并将其添加到 .eslintrc 配置文件中的 plugins 数组中。

    8. node/handle-callback-err

    此规则在回调中强制执行错误处理。

    1. // ❌
    2. function callback(err, data) {
    3. console.log(data);
    4. }
    5. // ✅
    6. function callback(err, data) {
    7. if (err) {
    8. console.log(err);
    9. return;
    10. }
    11. console.log(data);
    12. }

    在 Node.js 中,通常将错误作为第一个参数传递给回调函数。 忘记处理错误可能会导致应用程序行为异常。

    当函数的第一个参数命名为 err 时会触发此规则。可以通过在 .eslintrc 文件中为规则提供第二个参数来更改默认配置:

    1. node/handle-callback-err: ["error", "^(e|err|error)$"]

    9. node/no-callback-literal

    此规则强制使用 Error 对象作为第一个参数调用回调函数。 如果没有 Error,会接受 null 或 undefined 。

    1. // ❌
    2. cb('An error!');
    3. callback(result);
    4. // ✅
    5. cb(new Error('An error!'));
    6. callback(null, result);

    此规则可以确保不会意外调用以非 error 作为第一个参数的回调函数。 根据 error 优先回调约定,回调函数的第一个参数应该是 error,如果没有 Error,则为 null 或 undefined。

    仅当函数命名为 cb 或 callback 时才会触发此规则。

    10. node/no-sync

    此规则不允许在存在异步替代方案的 Node.js 核心 API 中使用同步方法。

    1. // ❌
    2. const file = fs.readFileSync(path);
    3. // ✅
    4. const file = await fs.readFile(path);

    在 Node.js 中对 I/O 操作使用同步方法会阻塞事件循环。 在大多数 Web 应用程序中,执行 I/O 操作时会使用异步方法。

    在某些应用程序中,可以使用同步方法。 可以在代码文件顶部使用以下代码来禁用此规则:

    1. /* eslint-disable node/no-sync */

    TypeScript 特定规则

    以下规则仅适用于 TypeScript 项目。

    11. @typescript-eslint/await-thenable

    此规则不允许 await 不是 Promise 的函数或值。

    1. // ❌
    2. function getValue() {
    3. return someValue;
    4. }
    5. await getValue();
    6. // ✅
    7. async function getValue() {
    8. return someValue;
    9. }
    10. await getValue();

    虽然在 JavaScript 中 await 非 Promise 值是有效的(将立即解析),但它通常表示代码逻辑是错误的,例如忘记添加括号来调用返回 Promise 的函数。

    12. @typescript-eslint/no-floating-promises

    此规则强制 Promises 添加错误处理程序。

    1. myPromise()
    2. .then(() => {});
    3. // ✅
    4. myPromise()
    5. .then(() => {})
    6. .catch(() => {});

    此规则可防止代码库中的浮动 Promises。 浮动 Promise 是没有任何代码来处理潜在错误的 Promise。

    13. @typescript-eslint/no-misused-promises

    此规则不允许将 Promise 传递到并非旨在处理它们的地方,例如 if 条件:

    1. // ❌
    2. if (getUserFromDB()) {}
    3. // ✅ 👎
    4. if (await getUserFromDB()) {}
    5. // ✅ 👍
    6. const user = await getUserFromDB();
    7. if (user) {}

    此规则可防止忘记添加 await 异步函数。虽然该规则确实允许在 if 条件中 await,但建议将结果分配给一个变量并在条件中使用该变量以提高可读性。

    14. @typescript-eslint/promise-function-async

    此规则强制返回 Promise 的是 async 函数。

    1. // ❌
    2. function doSomething() {
    3. return somePromise;
    4. }
    5. // ✅
    6. async function doSomething() {
    7. return somePromise;
    8. }

    返回 Promise 的非异步函数可能会抛出 Error 对象并返回被拒绝的 Promise。 通常不会编写代码来处理这两种情况。 此规则确保函数返回被拒绝的 Promise 或抛出 Error,但绝不会两者都有。

    以太坊cppgolang区别 编程

    以太坊cppgolang区别

    以太坊是一种去中心化的开源平台,它采用智能合约技术,旨在构建和运行不受干扰的分布式应用程序。作为目前最受欢迎的区块链平台之一,以太坊提供了多种编程语言的支持,其
    progolang 编程

    progolang

    Go语言(Golang)是由Google开发的一门静态类型编程语言。作为一名专业的Golang开发者,我深知这门语言的优势和特点。在本文中,我将介绍Golang
    golangn个发送者 编程

    golangn个发送者

    Golang是一种开源的编程语言,由Google团队开发,旨在提高程序的并发性和简化软件开发过程。在Go语言中,有时需要向多个接收者发送信息。本文将介绍如何在G
    golang技能图谱 编程

    golang技能图谱

    从互联网行业的快速发展到人工智能技术的日益成熟,各种编程语言也应运而生。而在这众多的编程语言中,Golang(即Go)作为一门强大且高效的开发语言备受关注。Go
    评论:0   参与:  7