mirror of
https://github.com/github/codeql-action.git
synced 2025-12-26 17:20:10 +08:00
151 lines
3.9 KiB
Markdown
151 lines
3.9 KiB
Markdown
[](https://www.npmjs.com/package/@open-draft/until)
|
|
|
|
# `until`
|
|
|
|
Gracefully handle a Promise using `async`/`await`.
|
|
|
|
## Why?
|
|
|
|
With the addition of `async`/`await` keywords in ECMAScript 2017 the handling of Promises became much easier. However, one must keep in mind that the `await` keyword provides no standard error handling API. Consider this usage:
|
|
|
|
```js
|
|
function getUser(id) {
|
|
const data = await fetchUser(id)
|
|
// Work with "data"...
|
|
}
|
|
```
|
|
|
|
In case `fetchUser()` throws an error, the entire `getUser()` function's scope will terminate. Because of this, it's recommended to implement error handling using `try`/`catch` block wrapping `await` expressions:
|
|
|
|
```js
|
|
function getUser(id)
|
|
let data = null
|
|
|
|
try {
|
|
data = await asyncAction()
|
|
} catch (error) {
|
|
console.error(error)
|
|
}
|
|
|
|
// Work with "data"...
|
|
}
|
|
```
|
|
|
|
While this is a semantically valid approach, constructing `try`/`catch` around each awaited operation may be tedious and get overlooked at times. Such error handling also introduces separate closures for execution and error scenarios of an asynchronous operation.
|
|
|
|
This library encapsulates the `try`/`catch` error handling in a utility function that does not create a separate closure and exposes a NodeJS-friendly API to work with errors and resolved data.
|
|
|
|
## Getting started
|
|
|
|
### Install
|
|
|
|
```bash
|
|
npm install @open-draft/until
|
|
```
|
|
|
|
### Usage
|
|
|
|
```js
|
|
import { until } from '@open-draft/until'
|
|
|
|
async function(id) {
|
|
const { error, data } = await until(() => fetchUser(id))
|
|
|
|
if (error) {
|
|
return handleError(error)
|
|
}
|
|
|
|
return data
|
|
}
|
|
```
|
|
|
|
### Usage with TypeScript
|
|
|
|
```ts
|
|
import { until } from '@open-draft/until'
|
|
|
|
interface User {
|
|
firstName: string
|
|
age: number
|
|
}
|
|
|
|
interface UserFetchError {
|
|
type: 'FORBIDDEN' | 'NOT_FOUND'
|
|
message?: string
|
|
}
|
|
|
|
async function(id: string) {
|
|
const { error, data } = await until<UserFetchError, User>(() => fetchUser(id))
|
|
|
|
if (error) {
|
|
handleError(error.type, error.message)
|
|
}
|
|
|
|
return data.firstName
|
|
}
|
|
```
|
|
|
|
## Frequently asked questions
|
|
|
|
### Why does `until` accept a function and not a `Promise` directly?
|
|
|
|
This has been intentionally introduced to await a single logical unit as opposed to a single `Promise`.
|
|
|
|
```js
|
|
// Notice how a single "until" invocation can handle
|
|
// a rather complex piece of logic. This way any rejections
|
|
// or exceptions happening within the given function
|
|
// can be handled via the same "error".
|
|
const { error, data } = until(async () => {
|
|
const user = await fetchUser()
|
|
const nextUser = normalizeUser(user)
|
|
const transaction = await saveModel('user', user)
|
|
|
|
invariant(transaction.status === 'OK', 'Saving user failed')
|
|
|
|
return transaction.result
|
|
})
|
|
|
|
if (error) {
|
|
// Handle any exceptions happened within the function.
|
|
}
|
|
```
|
|
|
|
### Why does `until` return an object and not an array?
|
|
|
|
The `until` function used to return an array of shape `[error, data]` prior to `2.0.0`. That has been changed, however, to get proper type-safety using discriminated union type.
|
|
|
|
Compare these two examples:
|
|
|
|
```ts
|
|
const [error, data] = await until(() => action())
|
|
|
|
if (error) {
|
|
return null
|
|
}
|
|
|
|
// Data still has ambiguous "DataType | null" type here
|
|
// even after you've checked and handled the "error" above.
|
|
console.log(data)
|
|
```
|
|
|
|
```ts
|
|
const result = await until(() => action())
|
|
|
|
// At this point, "data" is ambiguous "DataType | null"
|
|
// which is correct, as you haven't checked nor handled the "error".
|
|
|
|
if (result.error) {
|
|
return null
|
|
}
|
|
|
|
// Data is strict "DataType" since you've handled the "error" above.
|
|
console.log(result.data)
|
|
```
|
|
|
|
> It's crucial to keep the entire result of the `Promise` in a single variable and not destructure it. TypeScript will always keep the type of `error` and `data` as it was upon destructuring, ignoring any type guards you may perform later on.
|
|
|
|
## Special thanks
|
|
|
|
- [giuseppegurgone](https://twitter.com/giuseppegurgone) for the discussion about the original `until` API.
|