rslint_core/groups/errors/
no_await_in_loop.rs

1use crate::rule_prelude::*;
2use SyntaxKind::*;
3
4declare_lint! {
5    /**
6    Disallow await inside of loops.
7
8    You may want to `await` a promise until it is fulfilled or rejected, inside of loops. In such cases, to take
9    full advantage of concurrency, you should __not__ `await` the promise in every iteration, otherwise your async
10    operations will be executed serially.
11    Generally it is recommended that you create all promises, then use `Promise.all` for them. This way your async
12    operations will be performed concurrently.
13
14    ## Incorrect Code Exapmles
15
16    ```js
17    async function foo(xs) {
18        const results = [];
19        for (const x of xs) {
20            // iteration does not proceed until `bar(x)` completes
21            results.push(await bar(x));
22        }
23        return baz(results);
24    }
25    ```
26
27    ## Correct Code Examples
28
29    ```js
30    async function foo(xs) {
31        const results = [];
32        for (const x of xs) {
33            // push a promise to the array; it does not prevent the iteration
34            results.push(bar(x));
35        }
36        // we wait for all the promises concurrently
37        return baz(await Promise.all(results));
38    }
39    ```
40    */
41    #[derive(Default)]
42    NoAwaitInLoop,
43    errors,
44    tags(Recommended),
45    "no-await-in-loop"
46}
47
48#[typetag::serde]
49impl CstRule for NoAwaitInLoop {
50    fn check_node(&self, node: &SyntaxNode, ctx: &mut RuleCtx) -> Option<()> {
51        if let Some(err_node) = node.children().find(|node| node.kind() == AWAIT_EXPR) {
52            for ancestor in node.ancestors() {
53                match ancestor.kind() {
54                    FN_DECL | FN_EXPR | ARROW_EXPR => return None,
55                    FOR_OF_STMT if ancestor.to::<ast::ForOfStmt>().await_token().is_some() => {
56                        return None
57                    }
58                    _ => {}
59                }
60
61                if ancestor.is_loop()
62                    && ancestor
63                        .child_with_ast::<ast::Stmt>()?
64                        .range()
65                        .contains_range(node.text_range())
66                {
67                    let err = ctx.err(self.name(), "Unexpected `await` in loop")
68                        .primary(err_node, "this expression causes the loop to wait for the promise to resolve before continuing")
69                        .footer_note("the promises are resolved one after the other, not at the same time")
70                        .footer_help(format!("try adding the promises to an array, then resolving them all outside the loop using `{}`", color("Promise.all(/* promises */)")));
71
72                    ctx.add_err(err);
73                    return None;
74                }
75            }
76        }
77        None
78    }
79}
80
81rule_tests! {
82    NoAwaitInLoop::default(),
83    err: {
84        "
85        async function foo() {
86            const res = [];
87            for(var i = 1; i < 20; i++) {
88                res.push(await i);
89            }
90        }
91        ",
92        "
93        async () => {
94            while(true) {
95                await i;
96            }
97        }
98        "
99    },
100    ok: {
101        "
102        for (let i of await foo) {}
103        "
104    }
105}