/** * @fileoverview Rule to disallow uses of await inside of loops. * @author Nat Mote (nmote) */ "use strict"; // Node types which are considered loops. const loopTypes = new Set([ "ForStatement", "ForOfStatement", "ForInStatement", "WhileStatement", "DoWhileStatement" ]); /* * Node types at which we should stop looking for loops. For example, it is fine to declare an async * function within a loop, and use await inside of that. */ const boundaryTypes = new Set([ "FunctionDeclaration", "FunctionExpression", "ArrowFunctionExpression" ]); module.exports = { meta: { docs: { description: "disallow `await` inside of loops", category: "Possible Errors", recommended: false }, schema: [] }, create(context) { return { AwaitExpression(node) { const ancestors = context.getAncestors(); // Reverse so that we can traverse from the deepest node upwards. ancestors.reverse(); /* * Create a set of all the ancestors plus this node so that we can check * if this use of await appears in the body of the loop as opposed to * the right-hand side of a for...of, for example. */ const ancestorSet = new Set(ancestors).add(node); for (let i = 0; i < ancestors.length; i++) { const ancestor = ancestors[i]; if (boundaryTypes.has(ancestor.type)) { /* * Short-circuit out if we encounter a boundary type. Loops above * this do not matter. */ return; } if (loopTypes.has(ancestor.type)) { /* * Only report if we are actually in the body or another part that gets executed on * every iteration. */ if ( ancestorSet.has(ancestor.body) || ancestorSet.has(ancestor.test) || ancestorSet.has(ancestor.update) ) { context.report({ node, message: "Unexpected `await` inside a loop." }); return; } } } } }; } };