Skip to content
Dispatch Dispatch Dispatch
Go - Resumable functions

Resumable functions

Any function wrapped with dispatch.Func becomes a resumable function. This means that every function called via Await method becomes a checkpoint in the execution of that function. When an exception is raised, Dispatch will retry execution of the entire function from the last successful checkpoint.

For example, if function has three Await calls and the last one raised an exception, the next time that function is executed, it will restore the return values of the first two Await calls and will only re-run the third.

Consider this example application that implements a checkout flow. When user purchases a product, application charges the credit card, marks the order as completed and sends an email confirmation.

package main
import (
"context"
"encoding/json"
"log"
"net/http"
"github.com/dispatchrun/dispatch-go"
)
type SendEmailConfirmationInput struct {
UserId string `json:"user_id"`
OrderId string `json:"order_id"`
ReceiptId string `json:"receipt_id"`
}
func (input *SendEmailConfirmationInput) MarshalJSON() ([]byte, error) {
return json.Marshal(&struct {
UserId string `json:"user_id"`
OrderId string `json:"order_id"`
ReceiptId string `json:"receipt_id"`
}{
UserId: input.UserId,
OrderId: input.OrderId,
ReceiptId: input.ReceiptId,
})
}
func (input *SendEmailConfirmationInput) UnmarshalJSON(data []byte) error {
send_email_confirmation_input := &struct {
UserId string `json:"user_id"`
OrderId string `json:"order_id"`
ReceiptId string `json:"receipt_id"`
}{}
err := json.Unmarshal(data, &send_email_confirmation_input)
if err != nil {
return err
}
input.UserId = send_email_confirmation_input.UserId
input.OrderId = send_email_confirmation_input.OrderId
input.ReceiptId = send_email_confirmation_input.ReceiptId
return nil
}
type CheckoutFlowInput struct {
UserId string `json:"user_id"`
OrderId string `json:"order_id"`
}
func (input *CheckoutFlowInput) MarshalJSON() ([]byte, error) {
return json.Marshal(&struct {
UserId string `json:"user_id"`
OrderId string `json:"order_id"`
}{
UserId: input.UserId,
OrderId: input.OrderId,
})
}
func (input *CheckoutFlowInput) UnmarshalJSON(data []byte) error {
checkout_flow_input := &struct {
UserId string `json:"user_id"`
OrderId string `json:"order_id"`
}{}
err := json.Unmarshal(data, &checkout_flow_input)
if err != nil {
return err
}
input.UserId = checkout_flow_input.UserId
input.OrderId = checkout_flow_input.OrderId
return nil
}
func main() {
chargeCreditCard := dispatch.Func("chargeCreditCard", func(ctx context.Context, user_id string) (string, error) {
// Charge the credit card via Stripe, for example,
// and return the receipt ID afterwards
return "receipt_789", nil
})
sendEmailConfirmation := dispatch.Func("sendEmailConfirmation", func(ctx context.Context, input *SendEmailConfirmationInput) (any, error) {
// Send an email with order details
return nil, nil
})
markOrderAsCompleted := dispatch.Func("markOrderAsCompleted", func(ctx context.Context, order_id string) (any, error) {
// Update order in the database
return nil, nil
})
checkout := dispatch.Func("checkout", func(ctx context.Context, input *CheckoutFlowInput) (any, error) {
// Checkpoint 1
receiptId, err := chargeCreditCard.Await(input.UserId)
if err != nil {
return nil, err
}
// Checkpoint 2
_, err = sendEmailConfirmation.Await(&SendEmailConfirmationInput{
UserId: input.UserId,
OrderId: input.OrderId,
ReceiptId: receiptId,
})
if err != nil {
return nil, err
}
// Checkpoint 3
_, err = markOrderAsCompleted.Await(input.OrderId)
if err != nil {
return nil, err
}
return nil, nil
})
endpoint, err := dispatch.New(checkout, chargeCreditCard, sendEmailConfirmation, markOrderAsCompleted)
if err != nil {
log.Fatal(err)
}
http.Handle(endpoint.Handler())
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
checkout.Dispatch(r.Context(), &CheckoutFlowInput{
UserId: "user_123",
OrderId: "order_456",
})
w.WriteHeader(http.StatusOK)
})
if err := http.ListenAndServe("localhost:8000", nil); err != nil {
log.Fatal(err)
}
}

If sendEmailConfirmation raises an exception, checkout function will be executed again, but it won’t execute chargeCreditCard again. Instead, Dispatch restores receiptId from the previous execution and runs sendEmailConfirmation right away. No need to worry about charging user’s credit card twice.

Dispatch makes checkout function resumable, meaning that it picks up execution where it left off. This includes any other functions that checkout calls, as long as they’re also wrapped with dispatch.Func.

This powerful mechanism is so simple to integrate and yet it offers incredible value — Dispatch ensures that resumable functions always run to completion, no matter what failures it encounters.