Wait for event
Use step.waitForEvent()
to wait for a particular event to be received before continuing. It returns a Promise
that is resolved with the received event or null
if the event is not received within the timeout.
export default inngest.createFunction(
{ id: "send-onboarding-nudge-email" },
{ event: "app/account.created" },
async ({ event, step }) => {
const onboardingCompleted = await step.waitForEvent(
"wait-for-onboarding-completion",
{ event: "app/onboarding.completed", timeout: "3d", match: "data.userId" }
);
// Do something else
}
);
To add a simple time based delay to your code, use step.sleep()
instead.
step.waitForEvent(id, options): Promise<null | EventPayload>
- Name
id
- Type
- string
- Required
- required
- Description
The ID of the step. This will be what appears in your function's logs and is used to memoize step state across function versions.
- Name
options
- Type
- object
- Required
- required
- Description
Options for configuring how to wait for the event.
Properties- Name
event
- Type
- string
- Required
- required
- Description
The name of a given event to wait for.
- Name
timeout
- Type
- string
- Required
- required
- Description
The amount of time to wait to receive the event. A time string compatible with the ms package, e.g.
"30m"
,"3 hours"
, or"2.5d"
- Name
match
- Type
- string
- Required
- optional
- Description
The property to match the event trigger and the wait event, using dot-notation, e.g.
data.userId
. Cannot be combined withif
.
- Name
if
- Type
- string
- Required
- optional
- Description
An expression on which to conditionally match the original event trigger (
event
) and the wait event (async
). Cannot be combined withmatch
.**Expressions are defined using the Common Expression Language (CEL) with the events accessible using dot-notation. Read our guide to writing expressions for more info. Examples:
event.data.userId == async.data.userId && async.data.billing_plan == 'pro'
// Wait 7 days for an approval and match invoice IDs
const approval = await step.waitForEvent("wait-for-approval", {
event: "app/invoice.approved",
timeout: "7d",
match: "data.invoiceId",
});
// Wait 30 days for a user to start a subscription
// on the pro plan
const subscription = await step.waitForEvent("wait-for-subscription", {
event: "app/subscription.created",
timeout: "30d",
if: "event.data.userId == async.data.userId && async.data.billing_plan == 'pro'",
});
step.waitForEvent()
must be called using await
or some other Promise handler to ensure your function sleeps correctly.
Examples
Dynamic functions that wait for additional user actions
Below is an example of an Inngest function that creates an Intercom or Customer.io-like drip email campaign, customized based on
export default inngest.createFunction(
{ id: "onboarding-email-drip-campaign" },
{ event: "app/account.created" },
async ({ event, step }) => {
// Send the user the welcome email immediately
await step.run("send-welcome-email", async () => {
await sendEmail(event.user.email, "welcome");
});
// Wait up to 3 days for the user to complete the final onboarding step
// If the event is received within these 3 days, onboardingCompleted will be the
// event payload itself, if not it will be null
const onboardingCompleted = await step.waitForEvent("wait-for-onboarding", {
event: "app/onboarding.completed",
timeout: "3d",
// The "data.userId" must match in both the "app/account.created" and
// the "app/onboarding.completed" events
match: "data.userId",
});
// If the user has not completed onboarding within 3 days, send them a nudge email
if (!onboardingCompleted) {
await step.run("send-onboarding-nudge-email", async () => {
await sendEmail(event.user.email, "onboarding_nudge");
});
} else {
// If they have completed onboarding, send them a tips email
await step.run("send-tips-email", async () => {
await sendEmail(event.user.email, "new_user_tips");
});
}
}
);
Advanced event matching with if
For more complex functions, you may want to match the event payload against some other value. This could be a hard coded value like a billing plan name, a greater than filter for a number value or a value returned from a previous step.
In this example, we have built an AI blog post generator which returns three ideas to the user to select. Then when the user selects an idea from that batch of ideas, we generate an entire blog post and save it.
export default inngest.createFunction(
{ id: "generate-blog-post-with-ai" },
{ event: "ai/post.generator.requested" },
async ({ event, step }) => {
// Generate a number of suggestions for topics with OpenAI
const generatedTopics = await step.run("generate-topic-ideas", async () => {
const completion = await openai.createCompletion({
model: "text-davinci-003",
prompt: helpers.topicIdeaPromptWrapper(event.data.prompt),
n: 3,
});
return {
completionId: completion.data.id,
topics: completion.data.choices,
};
});
// Send the topics to the user via Websockets so they can select one
// Also send the completion id so we can match that later
await step.run("send-user-topics", () => {
pusher.sendToUser(event.data.userId, "topics_generated", {
sessionId: event.data.sessionId,
completionId: generatedTopics.completionId,
topics: generatedTopics.topics,
});
});
// Wait up to 5 minutes for the user to select a topic
// Ensuring the topic is from this batch of suggestions generated
const topicSelected = await step.waitForEvent("wait-for-topic-selection", {
event: "ai/post.topic.selected",
timeout: "5m",
// "async" is the "ai/post.topic.selected" event here:
if: `async.data.completionId == "${generatedTopics.completionId}"`,
});
// If the user selected a topic within 5 minutes, "topicSelected" will
// be the event payload, otherwise it is null
if (topicSelected) {
// Now that we've confirmed the user selected their topic idea from
// this batch of suggestions, let's generate a blog post
await step.run("generate-blog-post-draft", async () => {
const completion = await openai.createCompletion({
model: "text-davinci-003",
prompt: helpers.blogPostPromptWrapper(topicSelected.data.prompt),
});
// Do something with the blog post draft like save it or something else...
await blog.saveDraft(completion.data.choices[0]);
});
}
}
);