Feature: io.random() (#716)

* Add io.random

* Add changeset

---------

Co-authored-by: Eric Allam <eric@trigger.dev>
This commit is contained in:
nicktrn
2023-11-03 11:07:12 +00:00
committed by GitHub
parent 2ae5178479
commit cf8f994604
8 changed files with 194 additions and 1 deletions
+5
View File
@@ -0,0 +1,5 @@
---
"@trigger.dev/sdk": patch
---
Add `io.random()` which wraps `Math.random()` in a Task with helpful options.
+21
View File
@@ -0,0 +1,21 @@
```typescript Random example
client.defineJob({
id: "random-job",
name: "Random Job",
version: "0.0.1",
trigger: eventTrigger({
name: "example.event",
}),
run: async (payload, io, ctx) => {
// generate random numbers
const small = await io.random("random-small");
const large = await io.random("random-large", {
min: 10,
max: 200,
round: true
});
await io.logger.info(`${small} is smaller than ${large}`);
},
});
```
+1
View File
@@ -335,6 +335,7 @@
"sdk/io/logger",
"sdk/io/sendevent",
"sdk/io/backgroundfetch",
"sdk/io/random",
"sdk/io/try",
"sdk/io/registerinterval",
"sdk/io/unregisterinterval",
+4
View File
@@ -40,6 +40,10 @@ If you want to send an event from outside a run (e.g. just from your backend) yo
`io.backgroundFetch()` allows you to fetch data from a URL that can take longer that the serverless timeout. The actual `fetch` request is performed on the Trigger.dev platform, and the response is sent back to you. An example use case is fetching data from a slow API, like some AI endpoints.
### [random()](/sdk/io/random)
`io.random()` is identical to `Math.random()` when called without options but ensures your random numbers are not regenerated on resume or retry. It will return a pseudo-random floating-point number between optional `min` (default: 0, inclusive) and `max` (default: 1, exclusive). Can optionally `round` to the nearest integer.
### [try()](/sdk/io/try)
`io.try()` allows you to run Tasks and catch any errors that are thrown, it's similar to a normal `try/catch` block but works with [io.runTask()](/sdk/io/runtask).
+26
View File
@@ -0,0 +1,26 @@
---
title: "io.random()"
sidebarTitle: "random()"
description: "`io.random()` is identical to `Math.random()` when called without options but ensures your random numbers are not regenerated on resume or retry. It will return a pseudo-random floating-point number between optional `min` (default: 0, inclusive) and `max` (default: 1, exclusive). Can optionally `round` to the nearest integer."
---
## Parameters
<Snippet file="stable-key-param.mdx" />
<ResponseField name="min" type="number" default="0" required>
Sets the lower bound (inclusive). Can't be higher than `max`.
</ResponseField>
<ResponseField name="max" type="number" default="1" required>
Sets the upper bound (exclusive). Can't be lower than `min`.
</ResponseField>
<ResponseField name="round" type="boolean" default="false" required>
Controls rounding to the nearest integer. Any `max` integer will become inclusive when enabled. Rounding with floating-point bounds may cause unexpected skew and boundary inclusivity.
</ResponseField>
## Returns
A `Promise` that resolves with a pseudo-random number. Always resolves to an integer when rounding is enabled.
<RequestExample>
<Snippet file="random-example.mdx" />
</RequestExample>
+85 -1
View File
@@ -232,6 +232,90 @@ export class IO {
});
}
/** `io.random()` is identical to `Math.random()` when called without options but ensures your random numbers are not regenerated on resume or retry. It will return a pseudo-random floating-point number between optional `min` (default: 0, inclusive) and `max` (default: 1, exclusive). Can optionally `round` to the nearest integer.
* @param cacheKey Should be a stable and unique key inside the `run()`. See [resumability](https://trigger.dev/docs/documentation/concepts/resumability) for more information.
* @param min Sets the lower bound (inclusive). Can't be higher than `max`.
* @param max Sets the upper bound (exclusive). Can't be lower than `min`.
* @param round Controls rounding to the nearest integer. Any `max` integer will become inclusive when enabled. Rounding with floating-point bounds may cause unexpected skew and boundary inclusivity.
*/
async random(
cacheKey: string | any[],
{
min = 0,
max = 1,
round = false,
}: {
min?: number;
max?: number;
round?: boolean;
} = {}
) {
return await this.runTask(
cacheKey,
async (task) => {
if (min > max) {
throw new Error(
`Lower bound can't be higher than upper bound - min: ${min}, max: ${max}`
);
}
if (min === max) {
await this.logger.warn(
`Lower and upper bounds are identical. The return value is not random and will always be: ${min}`
);
}
const withinBounds = (max - min) * Math.random() + min;
if (!round) {
return withinBounds;
}
if (!Number.isInteger(min) || !Number.isInteger(max)) {
await this.logger.warn(
"Rounding enabled with floating-point bounds. This may cause unexpected skew and boundary inclusivity."
);
}
const rounded = Math.round(withinBounds);
return rounded;
},
{
name: "random",
icon: "dice-5-filled",
params: { min, max, round },
properties: [
...(min === 0
? []
: [
{
label: "min",
text: String(min),
},
]),
...(max === 1
? []
: [
{
label: "max",
text: String(max),
},
]),
...(round === false
? []
: [
{
label: "round",
text: String(round),
},
]),
],
style: { style: "minimal" },
}
);
}
/** `io.wait()` waits for the specified amount of time before continuing the Job. Delays work even if you're on a serverless platform with timeouts, or if your server goes down. They utilize [resumability](https://trigger.dev/docs/documentation/concepts/resumability) to ensure that the Run can be resumed after the delay.
* @param cacheKey Should be a stable and unique key inside the `run()`. See [resumability](https://trigger.dev/docs/documentation/concepts/resumability) for more information.
* @param seconds The number of seconds to wait. This can be very long, serverless timeouts are not an issue.
@@ -975,7 +1059,7 @@ export class IO {
*/
brb = this.yield.bind(this);
/** `io.try()` allows you to run Tasks and catch any errors that are thrown, it's similar to a normal `try/catch` block but works with [io.runTask()](/sdk/io/runtask).
/** `io.try()` allows you to run Tasks and catch any errors that are thrown, it's similar to a normal `try/catch` block but works with [io.runTask()](https://trigger.dev/docs/sdk/io/runtask).
* A regular `try/catch` block on its own won't work as expected with Tasks. Internally `runTask()` throws some special errors to control flow execution. This is necessary to deal with resumability, serverless timeouts, and retrying Tasks.
* @param tryCallback The code you wish to run
* @param catchCallback Thhis will be called if the Task fails. The callback receives the error
+1
View File
@@ -29,6 +29,7 @@
"misconfigured": "nodemon --watch src/misconfigured.ts -r tsconfig-paths/register -r dotenv/config src/misconfigured.ts",
"auto-yield": "nodemon --watch src/auto-yield.ts -r tsconfig-paths/register -r dotenv/config src/auto-yield.ts",
"cli-example": "nodemon --watch src/cli-example.ts -r tsconfig-paths/register -r dotenv/config src/cli-example.ts",
"random": "nodemon --watch src/random.ts -r tsconfig-paths/register -r dotenv/config src/random.ts",
"invoke": "nodemon --watch src/invoke.ts -r tsconfig-paths/register -r dotenv/config src/invoke.ts",
"dev:trigger": "trigger-cli dev --port 8080"
},
+51
View File
@@ -0,0 +1,51 @@
import { createExpressServer } from "@trigger.dev/express";
import { TriggerClient, eventTrigger } from "@trigger.dev/sdk";
export const client = new TriggerClient({
id: "job-catalog",
apiKey: process.env["TRIGGER_API_KEY"],
apiUrl: process.env["TRIGGER_API_URL"],
verbose: false,
ioLogLocalEnabled: true,
});
client.defineJob({
id: "random-example",
name: "Random Example",
version: "1.0.0",
enabled: true,
trigger: eventTrigger({
name: "random.example",
}),
run: async (payload, io, ctx) => {
// just like Math.random() but wrapped in a Task
await io.random("random-native");
// set lower and upper bounds - defaults to 0, 1 respectively
await io.random("random-min-max", { min: 10, max: 20 });
// set lower bound only (inclusive)
await io.random("random-min", { min: 0.5 });
// set upper bound only (exclusive)
await io.random("random-max", { max: 100 });
// round to the nearest integer
await io.random("random-round", { min: 100, max: 1000, round: true });
// rounding with floating-point bounds results in a warning
// this example will unexpectedly (but correctly!) output 1 or 2, skewing towards 2
await io.random("random-round-float", { min: 0.9, max: 2.5, round: true });
// negative values work just fine
await io.random("random-negative", { min: -100, max: -50 });
// identical lower and upper bounds result in a warning
await io.random("random-warn-bounds", { min: 10, max: 10 });
// invalid ranges will fail
await io.random("random-error", { min: 10, max: 5 });
},
});
createExpressServer(client);