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 | // babel-eslint transpiles AwaitExpressions to YieldExpressions, but the actual node kind is
|
27 | // still available in _babelType.
|
28 | YieldExpression: function(node) {
|
29 | if (node._babelType === 'AwaitExpression') {
|
30 | var ancestors = context.getAncestors();
|
31 | // Reverse so that we can traverse from the deepest node upwards.
|
32 | ancestors.reverse();
|
33 | // Create a set of all the ancestors plus this node so that we can check
|
34 | // if this use of await appears in the body of the loop as opposed to
|
35 | // the right-hand side of a for...of, for example.
|
36 | //
|
37 | // Implement the set with an Array since there are likely to be very few
|
38 | // elements. An Object would not be appropriate since the elements are
|
39 | // not strings.
|
40 | var ancestorSet = [].concat(ancestors, [node]);
|
41 | var ancestorSetHas = function(element) {
|
42 | return ancestorSet.indexOf(element) !== -1;
|
43 | }
|
44 | for (var i = 0; i < ancestors.length; i++) {
|
45 | var ancestor = ancestors[i];
|
46 | if (boundaryTypes.hasOwnProperty(ancestor.type)) {
|
47 | // Short-circuit out if we encounter a boundary type. Loops above
|
48 | // this do not matter.
|
49 | return;
|
50 | }
|
51 | if (loopTypes.hasOwnProperty(ancestor.type)) {
|
52 | // Only report if we are actually in the body or another part that gets executed on
|
53 | // every iteration.
|
54 | if (
|
55 | ancestorSetHas(ancestor.body) ||
|
56 | ancestorSetHas(ancestor.test) ||
|
57 | ancestorSetHas(ancestor.update)
|
58 | ) {
|
59 | context.report(
|
60 | node,
|
61 | 'Avoid using await inside a loop. Consider refactoring to use Promise.all. If ' +
|
62 | 'you are sure you want to do this, add `// eslint-disable-line ' +
|
63 | context.id + '` at the end of this line.'
|
64 | );
|
65 | return;
|
66 | }
|
67 | }
|
68 | }
|
69 | }
|
70 | },
|
71 | };
|
72 | }
|