UNPKG

2.7 kBJavaScriptView Raw
1/**
2 * @fileoverview Rule to disallow uses of await inside of loops.
3 * @author Nat Mote
4 */
5"use strict";
6
7// Node types which are considered loops.
8var 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.
18var boundaryTypes = {
19 'FunctionDeclaration': true,
20 'FunctionExpression': true,
21 'ArrowFunctionExpression': true,
22};
23
24module.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}