Skip to content

Commit a3be554

Browse files
Clement398fiskersindresorhus
authoredFeb 22, 2024
Add no-await-in-promise-methods rule (#2259)
Co-authored-by: fisker <lionkay@gmail.com> Co-authored-by: Sindre Sorhus <sindresorhus@gmail.com>
1 parent 4594020 commit a3be554

7 files changed

+390
-0
lines changed
 

‎configs/recommended.js

+1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ module.exports = {
2121
'unicorn/no-array-push-push': 'error',
2222
'unicorn/no-array-reduce': 'error',
2323
'unicorn/no-await-expression-member': 'error',
24+
'unicorn/no-await-in-promise-methods': 'error',
2425
'unicorn/no-console-spaces': 'error',
2526
'unicorn/no-document-cookie': 'error',
2627
'unicorn/no-empty-file': 'error',
+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# Disallow using `await` in `Promise` method parameters
2+
3+
💼 This rule is enabled in the ✅ `recommended` [config](https://github.com/sindresorhus/eslint-plugin-unicorn#preset-configs).
4+
5+
💡 This rule is manually fixable by [editor suggestions](https://eslint.org/docs/developer-guide/working-with-rules#providing-suggestions).
6+
7+
<!-- end auto-generated rule header -->
8+
<!-- Do not manually modify this header. Run: `npm run fix:eslint-docs` -->
9+
10+
Using `await` on promises passed as arguments to `Promise.all()`, `Promise.allSettled()`, `Promise.any()`, or `Promise.race()` is likely a mistake.
11+
12+
## Fail
13+
14+
```js
15+
Promise.all([await promise, anotherPromise]);
16+
17+
Promise.allSettled([await promise, anotherPromise]);
18+
19+
Promise.any([await promise, anotherPromise]);
20+
21+
Promise.race([await promise, anotherPromise]);
22+
```
23+
24+
## Pass
25+
26+
```js
27+
Promise.all([promise, anotherPromise]);
28+
29+
Promise.allSettled([promise, anotherPromise]);
30+
31+
Promise.any([promise, anotherPromise]);
32+
33+
Promise.race([promise, anotherPromise]);
34+
```

‎readme.md

+1
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ If you don't use the preset, ensure you use the same `env` and `parserOptions` c
131131
| [no-array-push-push](docs/rules/no-array-push-push.md) | Enforce combining multiple `Array#push()` into one call. || 🔧 | 💡 |
132132
| [no-array-reduce](docs/rules/no-array-reduce.md) | Disallow `Array#reduce()` and `Array#reduceRight()`. || | |
133133
| [no-await-expression-member](docs/rules/no-await-expression-member.md) | Disallow member access from await expression. || 🔧 | |
134+
| [no-await-in-promise-methods](docs/rules/no-await-in-promise-methods.md) | Disallow using `await` in `Promise` method parameters. || | 💡 |
134135
| [no-console-spaces](docs/rules/no-console-spaces.md) | Do not use leading/trailing space between `console.log` parameters. || 🔧 | |
135136
| [no-document-cookie](docs/rules/no-document-cookie.md) | Do not use `document.cookie` directly. || | |
136137
| [no-empty-file](docs/rules/no-empty-file.md) | Disallow empty files. || | |

‎rules/no-await-in-promise-methods.js

+68
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
'use strict';
2+
const {isMethodCall} = require('./ast/index.js');
3+
const {removeSpacesAfter} = require('./fix/index.js');
4+
5+
const MESSAGE_ID_ERROR = 'no-await-in-promise-methods/error';
6+
const MESSAGE_ID_SUGGESTION = 'no-await-in-promise-methods/suggestion';
7+
const messages = {
8+
[MESSAGE_ID_ERROR]: 'Promise in `Promise.{{method}}()` should not be awaited.',
9+
[MESSAGE_ID_SUGGESTION]: 'Remove `await`.',
10+
};
11+
const METHODS = ['all', 'allSettled', 'any', 'race'];
12+
13+
const isPromiseMethodCallWithArrayExpression = node =>
14+
isMethodCall(node, {
15+
object: 'Promise',
16+
methods: METHODS,
17+
optionalMember: false,
18+
optionalCall: false,
19+
argumentsLength: 1,
20+
})
21+
&& node.arguments[0].type === 'ArrayExpression';
22+
23+
/** @param {import('eslint').Rule.RuleContext} context */
24+
const create = context => ({
25+
* CallExpression(callExpression) {
26+
if (!isPromiseMethodCallWithArrayExpression(callExpression)) {
27+
return;
28+
}
29+
30+
for (const element of callExpression.arguments[0].elements) {
31+
if (element?.type !== 'AwaitExpression') {
32+
continue;
33+
}
34+
35+
yield {
36+
node: element,
37+
messageId: MESSAGE_ID_ERROR,
38+
data: {
39+
method: callExpression.callee.property.name,
40+
},
41+
suggest: [
42+
{
43+
messageId: MESSAGE_ID_SUGGESTION,
44+
* fix(fixer) {
45+
const {sourceCode} = context;
46+
const awaitToken = context.sourceCode.getFirstToken(element);
47+
yield fixer.remove(awaitToken);
48+
yield removeSpacesAfter(awaitToken, sourceCode, fixer);
49+
},
50+
},
51+
],
52+
};
53+
}
54+
},
55+
});
56+
57+
/** @type {import('eslint').Rule.RuleModule} */
58+
module.exports = {
59+
create,
60+
meta: {
61+
type: 'suggestion',
62+
docs: {
63+
description: 'Disallow using `await` in `Promise` method parameters.',
64+
},
65+
hasSuggestions: true,
66+
messages,
67+
},
68+
};

‎test/no-await-in-promise-methods.mjs

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import {getTester} from './utils/test.mjs';
2+
3+
const {test} = getTester(import.meta);
4+
5+
test.snapshot({
6+
valid: [
7+
'Promise.all([promise1, promise2, promise3, promise4])',
8+
'Promise.allSettled([promise1, promise2, promise3, promise4])',
9+
'Promise.any([promise1, promise2, promise3, promise4])',
10+
'Promise.race([promise1, promise2, promise3, promise4])',
11+
'Promise.all(...[await promise])',
12+
'Promise.all([await promise], extraArguments)',
13+
'Promise.all()',
14+
'Promise.all(notArrayExpression)',
15+
'Promise.all([,])',
16+
'Promise[all]([await promise])',
17+
'Promise.all?.([await promise])',
18+
'Promise?.all([await promise])',
19+
'Promise.notListedMethod([await promise])',
20+
'NotPromise.all([await promise])',
21+
'Promise.all([(await promise, 0)])',
22+
'new Promise.all([await promise])',
23+
24+
// We are not checking these cases
25+
'globalThis.Promise.all([await promise])',
26+
'Promise["all"]([await promise])',
27+
],
28+
invalid: [
29+
'Promise.all([await promise])',
30+
'Promise.allSettled([await promise])',
31+
'Promise.any([await promise])',
32+
'Promise.race([await promise])',
33+
'Promise.all([, await promise])',
34+
'Promise.all([await promise,])',
35+
'Promise.all([await promise],)',
36+
'Promise.all([await (0, promise)],)',
37+
'Promise.all([await (( promise ))])',
38+
'Promise.all([await await promise])',
39+
'Promise.all([...foo, await promise1, await promise2])',
40+
'Promise.all([await /* comment*/ promise])',
41+
],
42+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,244 @@
1+
# Snapshot report for `test/no-await-in-promise-methods.mjs`
2+
3+
The actual snapshot is saved in `no-await-in-promise-methods.mjs.snap`.
4+
5+
Generated by [AVA](https://avajs.dev).
6+
7+
## invalid(1): Promise.all([await promise])
8+
9+
> Input
10+
11+
`␊
12+
1 | Promise.all([await promise])␊
13+
`
14+
15+
> Error 1/1
16+
17+
`␊
18+
> 1 | Promise.all([await promise])␊
19+
| ^^^^^^^^^^^^^ Promise in \`Promise.all()\` should not be awaited.␊
20+
21+
--------------------------------------------------------------------------------␊
22+
Suggestion 1/1: Remove \`await\`.␊
23+
1 | Promise.all([promise])␊
24+
`
25+
26+
## invalid(2): Promise.allSettled([await promise])
27+
28+
> Input
29+
30+
`␊
31+
1 | Promise.allSettled([await promise])␊
32+
`
33+
34+
> Error 1/1
35+
36+
`␊
37+
> 1 | Promise.allSettled([await promise])␊
38+
| ^^^^^^^^^^^^^ Promise in \`Promise.allSettled()\` should not be awaited.␊
39+
40+
--------------------------------------------------------------------------------␊
41+
Suggestion 1/1: Remove \`await\`.␊
42+
1 | Promise.allSettled([promise])␊
43+
`
44+
45+
## invalid(3): Promise.any([await promise])
46+
47+
> Input
48+
49+
`␊
50+
1 | Promise.any([await promise])␊
51+
`
52+
53+
> Error 1/1
54+
55+
`␊
56+
> 1 | Promise.any([await promise])␊
57+
| ^^^^^^^^^^^^^ Promise in \`Promise.any()\` should not be awaited.␊
58+
59+
--------------------------------------------------------------------------------␊
60+
Suggestion 1/1: Remove \`await\`.␊
61+
1 | Promise.any([promise])␊
62+
`
63+
64+
## invalid(4): Promise.race([await promise])
65+
66+
> Input
67+
68+
`␊
69+
1 | Promise.race([await promise])␊
70+
`
71+
72+
> Error 1/1
73+
74+
`␊
75+
> 1 | Promise.race([await promise])␊
76+
| ^^^^^^^^^^^^^ Promise in \`Promise.race()\` should not be awaited.␊
77+
78+
--------------------------------------------------------------------------------␊
79+
Suggestion 1/1: Remove \`await\`.␊
80+
1 | Promise.race([promise])␊
81+
`
82+
83+
## invalid(5): Promise.all([, await promise])
84+
85+
> Input
86+
87+
`␊
88+
1 | Promise.all([, await promise])␊
89+
`
90+
91+
> Error 1/1
92+
93+
`␊
94+
> 1 | Promise.all([, await promise])␊
95+
| ^^^^^^^^^^^^^ Promise in \`Promise.all()\` should not be awaited.␊
96+
97+
--------------------------------------------------------------------------------␊
98+
Suggestion 1/1: Remove \`await\`.␊
99+
1 | Promise.all([, promise])␊
100+
`
101+
102+
## invalid(6): Promise.all([await promise,])
103+
104+
> Input
105+
106+
`␊
107+
1 | Promise.all([await promise,])␊
108+
`
109+
110+
> Error 1/1
111+
112+
`␊
113+
> 1 | Promise.all([await promise,])␊
114+
| ^^^^^^^^^^^^^ Promise in \`Promise.all()\` should not be awaited.␊
115+
116+
--------------------------------------------------------------------------------␊
117+
Suggestion 1/1: Remove \`await\`.␊
118+
1 | Promise.all([promise,])␊
119+
`
120+
121+
## invalid(7): Promise.all([await promise],)
122+
123+
> Input
124+
125+
`␊
126+
1 | Promise.all([await promise],)␊
127+
`
128+
129+
> Error 1/1
130+
131+
`␊
132+
> 1 | Promise.all([await promise],)␊
133+
| ^^^^^^^^^^^^^ Promise in \`Promise.all()\` should not be awaited.␊
134+
135+
--------------------------------------------------------------------------------␊
136+
Suggestion 1/1: Remove \`await\`.␊
137+
1 | Promise.all([promise],)␊
138+
`
139+
140+
## invalid(8): Promise.all([await (0, promise)],)
141+
142+
> Input
143+
144+
`␊
145+
1 | Promise.all([await (0, promise)],)␊
146+
`
147+
148+
> Error 1/1
149+
150+
`␊
151+
> 1 | Promise.all([await (0, promise)],)␊
152+
| ^^^^^^^^^^^^^^^^^^ Promise in \`Promise.all()\` should not be awaited.␊
153+
154+
--------------------------------------------------------------------------------␊
155+
Suggestion 1/1: Remove \`await\`.␊
156+
1 | Promise.all([(0, promise)],)␊
157+
`
158+
159+
## invalid(9): Promise.all([await (( promise ))])
160+
161+
> Input
162+
163+
`␊
164+
1 | Promise.all([await (( promise ))])␊
165+
`
166+
167+
> Error 1/1
168+
169+
`␊
170+
> 1 | Promise.all([await (( promise ))])␊
171+
| ^^^^^^^^^^^^^^^^^^^ Promise in \`Promise.all()\` should not be awaited.␊
172+
173+
--------------------------------------------------------------------------------␊
174+
Suggestion 1/1: Remove \`await\`.␊
175+
1 | Promise.all([(( promise ))])␊
176+
`
177+
178+
## invalid(10): Promise.all([await await promise])
179+
180+
> Input
181+
182+
`␊
183+
1 | Promise.all([await await promise])␊
184+
`
185+
186+
> Error 1/1
187+
188+
`␊
189+
> 1 | Promise.all([await await promise])␊
190+
| ^^^^^^^^^^^^^^^^^^^ Promise in \`Promise.all()\` should not be awaited.␊
191+
192+
--------------------------------------------------------------------------------␊
193+
Suggestion 1/1: Remove \`await\`.␊
194+
1 | Promise.all([await promise])␊
195+
`
196+
197+
## invalid(11): Promise.all([...foo, await promise1, await promise2])
198+
199+
> Input
200+
201+
`␊
202+
1 | Promise.all([...foo, await promise1, await promise2])␊
203+
`
204+
205+
> Error 1/2
206+
207+
`␊
208+
> 1 | Promise.all([...foo, await promise1, await promise2])␊
209+
| ^^^^^^^^^^^^^^ Promise in \`Promise.all()\` should not be awaited.␊
210+
211+
--------------------------------------------------------------------------------␊
212+
Suggestion 1/1: Remove \`await\`.␊
213+
1 | Promise.all([...foo, promise1, await promise2])␊
214+
`
215+
216+
> Error 2/2
217+
218+
`␊
219+
> 1 | Promise.all([...foo, await promise1, await promise2])␊
220+
| ^^^^^^^^^^^^^^ Promise in \`Promise.all()\` should not be awaited.␊
221+
222+
--------------------------------------------------------------------------------␊
223+
Suggestion 1/1: Remove \`await\`.␊
224+
1 | Promise.all([...foo, await promise1, promise2])␊
225+
`
226+
227+
## invalid(12): Promise.all([await /* comment*/ promise])
228+
229+
> Input
230+
231+
`␊
232+
1 | Promise.all([await /* comment*/ promise])␊
233+
`
234+
235+
> Error 1/1
236+
237+
`␊
238+
> 1 | Promise.all([await /* comment*/ promise])␊
239+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ Promise in \`Promise.all()\` should not be awaited.␊
240+
241+
--------------------------------------------------------------------------------␊
242+
Suggestion 1/1: Remove \`await\`.␊
243+
1 | Promise.all([/* comment*/ promise])␊
244+
`
723 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)
Please sign in to comment.