You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
@@ -53,41 +59,22 @@ async function logUserDetails(userId) {
53
59
54
60
As we can see above, an `async` function is defined via the `async` keyword right before the function's parameter list. In the function body, we are now able to use the `await` keyword to explicitly wait for a `Promise` value and assign its content to a let binding `email`.
55
61
56
-
Everything we've just saw was essentially what we are used to `async` / `await` in JS, but there's still a few details that are specific to ReScript. The next few sections will go through all the details that are specific to the ReScript type system.
62
+
You will probably notice that this looks very similar to `async` / `await` in JS, but there's still a few details that are specific to ReScript. The next few sections will go through all the details that are specific to the ReScript type system.
57
63
58
-
## Types and `async` functions
64
+
## Basics
59
65
60
-
### No `promise` type in inline return types
61
-
62
-
When typing the return type of an `async` function inline, we completely omit the `promise<...>` type and just state the actual type we want to return. As an example, we would type a `logUserDetails` function like this:
63
-
64
-
65
-
```res
66
-
// Instead of promise<unit> we return `unit` instead.
67
-
// The boxing into a promise is already done implicitly
68
-
// by the compiler.
69
-
let logUserDetails = async (userId: string): unit => {
70
-
Js.log("...")
71
-
}
72
-
```
73
-
74
-
**Note:** This was a deliberate design decision. More details on the rationale can be found [here](https://github.com/rescript-lang/rescript-compiler/pull/5913#issuecomment-1359003870).
75
-
76
-
### Promises don't auto-collapse in async functions
66
+
- You may only use `await` in `async` function bodies
67
+
-`await` may only be called on a `promise` value
68
+
-`await` calls are expressions, therefore they can be used in pattern matching (`switch`)
69
+
- A function returning a `promise<'a>` is equivalent to an `async` function returning a value `'a` (important for writing signature files and bindings)
70
+
-`promise` values and types returned from an `async` function don't auto-collapse
77
71
78
-
As a JS developer you'd expect a `promise<'a>` to collapse into another `promise<'a>` when returned in an `async` function. This is not the case in ReScript. Use the `await` function to unwrap any nested promises instead.
79
72
80
-
```res
81
-
let fetchData = async (userId: string): string => {
82
-
// We can't just return the result of `fetchUserMail`, otherwise we'd get a
83
-
// type error due to our function return type of type `string`
84
-
await fetchUserMail(userId)
85
-
}
86
-
```
73
+
## Types and `async` functions
87
74
88
75
### `async` function type signatures
89
76
90
-
Function type signatures (i.e defined in signature files) don't differentiate between `async`and conventional functions. Every function with a `promise` return type are`async`functions; hence we use the`promise` return type.
77
+
Function type signatures (i.e defined in signature files) don't require any special keywords for `async`usage. Whenever you want to type an`async`function, use a`promise` return type.
91
78
92
79
```resi
93
80
// Demo.resi
@@ -97,7 +84,7 @@ let fetchUserMail: string => promise<string>
97
84
98
85
The same logic applies to type definitions in `.res` files:
(The last example was only mentioned for education purposes. Don't do that in your actual code.)
116
+
**Note:** In a practical scenario you'd either use a type signature, or inline types, not both at the same time. In case you are interested in the design decisions, check out [this discussion](https://github.com/rescript-lang/rescript-compiler/pull/5913#issuecomment-1359003870).
120
117
121
-
##Common usage examples
118
+
### `async` uncurried functions
122
119
123
-
### Error handling
120
+
The `async` keyword does also work for uncurried functions.
124
121
125
-
As with any synchronous code, you may use `try / catch` or `switch` to pattern match on errors.
122
+
```res
123
+
let fetchData = async (. userId: string): string {
124
+
await fetchUserMail(userId)
125
+
}
126
+
```
126
127
127
-
```res example
128
-
let logUserDetails = async (userId: string): result<unit, string> => {
129
-
let email = await fetchUserMail(userId)
128
+
### Promises don't auto-collapse in async functions
129
+
130
+
In JS, nested promises (i.e. `promise<promise<'a>>`) will automatically collapse into a flat promise (`promise<'a>`). This is not the case in ReScript. Use the `await` function to manually unwrap any nested promises within an `async` function instead.
131
+
132
+
```res
133
+
let fetchData = async (userId: string): string => {
134
+
// We can't just return the result of `fetchUserMail`, otherwise we'd get a
135
+
// type error due to our function return type of type `string`
136
+
await fetchUserMail(userId)
137
+
}
138
+
```
130
139
131
-
// await can be used within a `try` body
140
+
## Error handling
141
+
142
+
You may use `try / catch` or `switch` to handle exceptions during async execution.
| None => Js.log("Some other exception has been thrown")
179
+
}
180
+
}
181
+
}
182
+
```
183
+
184
+
**Important:** When using `await` with a `switch`, always make sure to put the actual await call in the `switch` expression, otherwise your `await` error will not be caught.
185
+
186
+
## Piping `await` calls
187
+
188
+
You may want to pipe the result of an `await` call right into another function.
189
+
This can be done by wrapping your `await` calls in a new `{}` closure.
146
190
147
191
<CodeTablabels={["ReScript", "JS Output"]}>
148
192
@@ -164,9 +208,11 @@ async function fetchData(param) {
164
208
165
209
</CodeTab>
166
210
167
-
### Pattern matching on `await` calls
211
+
Note how the original closure was removed in the final JS output. No extra allocations!
168
212
169
-
Of course we can also go fancy with all kinds of pattern matching combinations.
213
+
## Pattern matching on `await` calls
214
+
215
+
`await` calls are just another kind of expression, so you can use `switch` pattern matching for more complex logic.
170
216
171
217
<CodeTablabels={["ReScript", "JS Output"]}>
172
218
@@ -208,3 +254,87 @@ async function fetchData(param) {
208
254
209
255
</CodeTab>
210
256
257
+
## `await` multiple promises
258
+
259
+
We can utilize the `Js.Promise2` module to handle multiple promises. E.g. let's use `Js.Promise2.all` to wait for multiple promises before continuing the program:
260
+
261
+
```res
262
+
let pauseReturn = async (value, timeout) => {
263
+
Js.Promise2.make((~resolve, ~reject) => {
264
+
Js.Global.setTimeout(() => {
265
+
resolve(. value)
266
+
}, timeout)->ignore
267
+
})
268
+
}
269
+
270
+
let logMultipleValues = async () => {
271
+
let value1 = await pauseReturn("value1", 2000)
272
+
let value2 = await pauseReturn("value2", 1200)
273
+
let value3 = await pauseReturn("value3", 500)
274
+
275
+
let all = await Js.Promise2.all([value1, value2, value3])
`async` / `await` practically works with any function that returns a `promise<'a>` value. Map your `promise` returning function via an `external`, and use it in an `async` function as usual.
287
+
288
+
Here's a full example of using the MDN `fetch` API, using `async` / `await` to simulate a login:
289
+
290
+
```res
291
+
// A generic Response type for typing our fetch requests
0 commit comments