no-await-in-loop.js 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  1. /**
  2. * @fileoverview Rule to disallow uses of await inside of loops.
  3. * @author Nat Mote (nmote)
  4. */
  5. "use strict";
  6. /**
  7. * Check whether it should stop traversing ancestors at the given node.
  8. * @param {ASTNode} node A node to check.
  9. * @returns {boolean} `true` if it should stop traversing.
  10. */
  11. function isBoundary(node) {
  12. const t = node.type;
  13. return (
  14. t === "FunctionDeclaration" ||
  15. t === "FunctionExpression" ||
  16. t === "ArrowFunctionExpression" ||
  17. /*
  18. * Don't report the await expressions on for-await-of loop since it's
  19. * asynchronous iteration intentionally.
  20. */
  21. (t === "ForOfStatement" && node.await === true)
  22. );
  23. }
  24. /**
  25. * Check whether the given node is in loop.
  26. * @param {ASTNode} node A node to check.
  27. * @param {ASTNode} parent A parent node to check.
  28. * @returns {boolean} `true` if the node is in loop.
  29. */
  30. function isLooped(node, parent) {
  31. switch (parent.type) {
  32. case "ForStatement":
  33. return (
  34. node === parent.test ||
  35. node === parent.update ||
  36. node === parent.body
  37. );
  38. case "ForOfStatement":
  39. case "ForInStatement":
  40. return node === parent.body;
  41. case "WhileStatement":
  42. case "DoWhileStatement":
  43. return node === parent.test || node === parent.body;
  44. default:
  45. return false;
  46. }
  47. }
  48. /** @type {import('../shared/types').Rule} */
  49. module.exports = {
  50. meta: {
  51. type: "problem",
  52. docs: {
  53. description: "Disallow `await` inside of loops",
  54. recommended: false,
  55. url: "https://eslint.org/docs/rules/no-await-in-loop"
  56. },
  57. schema: [],
  58. messages: {
  59. unexpectedAwait: "Unexpected `await` inside a loop."
  60. }
  61. },
  62. create(context) {
  63. /**
  64. * Validate an await expression.
  65. * @param {ASTNode} awaitNode An AwaitExpression or ForOfStatement node to validate.
  66. * @returns {void}
  67. */
  68. function validate(awaitNode) {
  69. if (awaitNode.type === "ForOfStatement" && !awaitNode.await) {
  70. return;
  71. }
  72. let node = awaitNode;
  73. let parent = node.parent;
  74. while (parent && !isBoundary(parent)) {
  75. if (isLooped(node, parent)) {
  76. context.report({
  77. node: awaitNode,
  78. messageId: "unexpectedAwait"
  79. });
  80. return;
  81. }
  82. node = parent;
  83. parent = parent.parent;
  84. }
  85. }
  86. return {
  87. AwaitExpression: validate,
  88. ForOfStatement: validate
  89. };
  90. }
  91. };