1 | /**
|
2 | * @fileoverview Rule to disallow uses of await inside of loops.
|
3 | * @author Nat Mote
|
4 | */
|
5 | ;
|
6 |
|
7 | // Node types which are considered loops.
|
8 | var loopTypes = {
|
9 | 'ForStatement': true,
|
10 | 'ForOfStatement': true,
|
11 | 'ForInStatement': true,
|
12 | 'WhileStatement': true,
|
13 | 'DoWhileStatement': true,
|
14 | };
|
15 |
|
16 | // Node types at which we should stop looking for loops. For example, it is fine to declare an async
|
17 | // function within a loop, and use await inside of that.
|
18 | var boundaryTypes = {
|
19 | 'FunctionDeclaration': true,
|
20 | 'FunctionExpression': true,
|
21 | 'ArrowFunctionExpression': true,
|
22 | };
|
23 |
|
24 | module.exports = function(context) {
|
25 | return {
|
26 | AwaitExpression(node) {
|
27 | var ancestors = context.getAncestors();
|
28 | // Reverse so that we can traverse from the deepest node upwards.
|
29 | ancestors.reverse();
|
30 | // Create a set of all the ancestors plus this node so that we can check
|
31 | // if this use of await appears in the body of the loop as opposed to
|
32 | // the right-hand side of a for...of, for example.
|
33 | //
|
34 | // Implement the set with an Array since there are likely to be very few
|
35 | // elements. An Object would not be appropriate since the elements are
|
36 | // not strings.
|
37 | var ancestorSet = [].concat(ancestors, [node]);
|
38 | var ancestorSetHas = function(element) {
|
39 | return ancestorSet.indexOf(element) !== -1;
|
40 | }
|
41 | for (var i = 0; i < ancestors.length; i++) {
|
42 | var ancestor = ancestors[i];
|
43 | if (boundaryTypes.hasOwnProperty(ancestor.type)) {
|
44 | // Short-circuit out if we encounter a boundary type. Loops above
|
45 | // this do not matter.
|
46 | return;
|
47 | }
|
48 | if (loopTypes.hasOwnProperty(ancestor.type)) {
|
49 | // Only report if we are actually in the body or another part that gets executed on
|
50 | // every iteration.
|
51 | if (
|
52 | ancestorSetHas(ancestor.body) ||
|
53 | ancestorSetHas(ancestor.test) ||
|
54 | ancestorSetHas(ancestor.update)
|
55 | ) {
|
56 | context.report(
|
57 | node,
|
58 | 'Avoid using await inside a loop. Consider refactoring to use Promise.all. If ' +
|
59 | 'you are sure you want to do this, add `// eslint-disable-line ' +
|
60 | context.id + '` at the end of this line.'
|
61 | );
|
62 | return;
|
63 | }
|
64 | }
|
65 | }
|
66 | },
|
67 | };
|
68 | }
|