当前位置:   article > 正文

JavaScript 常见的规范异步代码的ESLint 规则_require-atomic-updates

require-atomic-updates

异步操作包括:

  • 事件循环
  • 回调函数
  • promise 和 async/await

ESLint 中文文档:https://www.tkcnn.com/eslint/rules/no-promise-executor-return.html

no-async-promise-executor

报错解释:

no-async-promise-executor 用于检测在创建 Promise 时,其执行器(executor)函数是否包含异步操作。

不应该将一个异步函数(async函数)直接传递给Promise的构造函数。因为JavaScript中的Promise构造函数接受的应该是一个同步的执行函数,它不应该执行异步操作。

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

JavaScript中的Promise是用来进行异步编程的一个重要机制。Promise对象代表一个即将完成、失败或进行中的操作。Promise构造函数接受一个执行函数作为参数,这个执行函数接受两个参数:resolve和reject。resolve函数在操作成功完成时调用,而reject函数在操作失败时调用。

异步函数使用async关键字定义,它自动返回一个Promise对象。当你在async函数中执行异步操作时,它会返回一个Promise对象,该对象在未来某个时刻将会被解决或拒绝。

问题在于,如果你将一个async函数直接传递给Promise构造函数,实际上你会得到一个立即解决的Promise,其值就是这个async函数的返回值,而这个返回值实际上是另一个Promise。这样会导致逻辑上的混乱,并可能不按预期工作。另外,如果 async 函数抛出了异常,新构造的 promise 实例并不会 reject ,那么这个错误就捕获不到了。

解决方法:

  1. 如果确实需要在 Promise 的执行器中进行异步操作,确保正确地处理 Promise 链,使用 async/await 来等待异步操作完成。
  2. 如果执行器不需要异步操作,移除任何返回 Promise 的调用,确保执行器函数是同步的
  1. // 正确的使用Promise的方式:移除异步操作
  2. const doSomethingAsync = () => {
  3. return new Promise((resolve, reject) => {
  4. // 在这里执行异步操作
  5. someAsyncOperation((error, result) => {
  6. if (error) {
  7. reject(error);
  8. } else {
  9. resolve(result);
  10. }
  11. });
  12. });
  13. };

而不是将async函数直接传递给Promise构造函数:

  1. // 这样做是错误的
  2. const asyncFunction = async () => {
  3. // 异步操作
  4. };
  5. // 这里会创建一个立即解决的Promise
  6. const wrongPromise = new Promise(asyncFunction);

如果你需要将一个现有的async函数转化为Promise,你可以直接调用这个async函数,它会返回一个Promise对象。例如:

  1. const asyncFunction = async () => {
  2. // 异步操作
  3. };
  4. const promise = asyncFunction();

总结:避免将async函数传递给Promise构造函数,而是直接调用async函数来获取返回的Promise对象。

no-await-in-loop

不建议在循环里使用 await,有这种写法通常意味着程序没有充分利用 JavaScript 的事件驱动。

在迭代器的每个元素上执行运算是个常见的任务。然而,每次运算都执行 await,意味着该程序并没有充分利用 async/await 并行的好处。

通常,代码应该重构为立即创建所有 promise,然后通过 Promise.all() 访问结果。否则,每个后续的操作将不会执行,直到前一个操作执行完毕。

  1. // ❌
  2. for (const url of urls) {
  3. const response = await fetch(url);
  4. }

建议将这些异步任务改为并发执行,这可以大大提升代码的执行效率。

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

no-promise-executor-return

no-promise-executor-return表示禁止从 Promise 执行器函数返回值。根据这条规则,不建议在 Promise 构造函数中返回值,Promise 构造函数中返回的值是没法用的,并且返回值也不会影响到 Promise 的状态。相反,它们应该只使用执行器参数提供的resolve和reject函数。

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

正常的做法是将返回值传递给 resolve,如果出错了就传给 reject

  1. //
  2. const myPromise = new Promise((resolve, reject) => {
  3. // Do some asynchronous operations
  4. if (/* operation successful */) {
  5. resolve(someValue);
  6. } else {
  7. reject(new Error('Operation failed'));
  8. }
  9. });

遵循此规则可确保基于承诺的代码保持清晰,并遵循预期的异步流,而不依赖于执行器函数本身的返回值。

require-atomic-updates

require-atomic-updates是Node.js和JavaScript中的一个规则,它强调了依赖异步操作但不能正确处理它们的代码的潜在问题。具体来说,它解决了以可能导致竞争条件或意外行为的方式访问或修改变量的情况。
此规则有助于确保正确处理异步操作,特别是在处理共享资源或可变状态时。不建议将赋值操作和 await 组合使用,这可能会导致条件竞争。

“require-atomic-updates”的名称表明在处理异步操作时原子更新是必需的。原子更新是一种完全发生或根本不发生的操作,不会被其他操作中断。在JavaScript上下文中,这通常涉及使用适当的同步机制,如锁、互斥锁或原子操作,以确保安全地访问或修改共享资源。

下面是一个可能触发require-atomic-updates规则的代码示例:

  1. let counter = 0;
  2. async function incrementCounter() {
  3. counter++; // This operation is not atomic
  4. }
  5. incrementCounter();

在本例中,计数器++操作不是原子的,这意味着它不能保证完全不中断地执行,特别是在异步操作的上下文中。为了解决这个问题,可以使用锁或互斥锁等同步机制来确保操作是原子性的,并且可以安全地访问共享资源。

  1. let counter = 0;
  2. const mutex = new Mutex();
  3. async function incrementCounter() {
  4. await mutex.lock();
  5. counter++;
  6. mutex.unlock();
  7. }
  8. incrementCounter();

max-nested-callbacks

许多JavaScript库使用回调模式来管理异步操作。任何复杂性的程序都很可能需要管理多个并发级别的异步操作。容易陷入的一个常见陷阱是嵌套回调,这使得代码更难以更深地读取回调嵌套。

此规则防止回调地狱,避免大量的深度嵌套:

  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);

回调地狱让代码难以阅读和维护,建议将回调都重构为 Promise 并使用现代的 async/await 语法。

no-return-await

禁止不必要的 return await。返回异步结果时不一定要写 await ,如果你要等待一个 Promise,然后又要立刻返回它,这可能是不必要的。通过避免不必要的承诺链来提高代码的可读性和性能。
在async函数中使用return await时,实际上是在等待promise解析后返回其结果。然而,由于异步函数已经返回承诺,可以直接返回承诺而不使用await。

  1. // ❌
  2. async () => {
  3. return await getUser(userId);
  4. }

从一个 async 函数返回的所有值都包含在一个 Promise 中,你可以直接返回这个 Promise

  1. //
  2. async () => {
  3. return getUser(userId);
  4. }

当然,也有个例外,如果外面有 try...catch 包裹,删除 await 就捕获不到异常了,在这种情况下,建议明确一下意图,把结果分配给不同行的变量。

  1. //
    声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小桥流水78/article/detail/1010113
    推荐阅读
    相关标签