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