[GH-ISSUE #285] [FEATURE REQUEST] Returning result from handler along with error #2138

Closed
opened 2026-03-15 19:20:47 +03:00 by kerem · 4 comments
Owner

Originally created by @sujit-baniya on GitHub (Jun 28, 2021).
Original GitHub issue: https://github.com/hibiken/asynq/issues/285

Originally assigned to: @hibiken on GitHub.

Is your feature request related to a problem? Please describe.
No

Describe the solution you'd like
Currently the signature of a handler is func(ctx context.Context, t *asynq.Task) error. When using middleware, I would like to get result of the function and handle the code further.

I suppose following signature would work

func (ctx context.Context, t *asynq.Task) ([]byte, error)
Originally created by @sujit-baniya on GitHub (Jun 28, 2021). Original GitHub issue: https://github.com/hibiken/asynq/issues/285 Originally assigned to: @hibiken on GitHub. **Is your feature request related to a problem? Please describe.** No **Describe the solution you'd like** Currently the signature of a handler is `func(ctx context.Context, t *asynq.Task) error`. When using middleware, I would like to get result of the function and handle the code further. I suppose following signature would work ```go func (ctx context.Context, t *asynq.Task) ([]byte, error) ```
kerem 2026-03-15 19:20:47 +03:00
Author
Owner

@crossworth commented on GitHub (Jun 28, 2021):

Hi, do you want to handle the code further only on error or even on success?

If it's only on error, you could probably create a custom error with the data you need, return it and type assert on your middleware.

If you want even with success, I think a better approach would be using context.WithValue, maybe changing Task to have the context on it.

// Task represents a unit of work to be performed.
type Task struct {
	// Type indicates the type of task to be performed.
	Type string

	// Payload holds data needed to perform the task.
	Payload Payload
	
	Context context.Context
}

With this approach we could easily do:

// inside the task handler
// t *asynq.Task
t.Context = context.WithValue(t.Context, "myKey", "myValue")

and retrieve the value in the middleware.

err := h.ProcessTask(t)
if err != nil {
	return err
}

fmt.Println("Val: ", t.Context.Value("myKey"))

I think this way is more idiomatic and similar of how the http requests works.

<!-- gh-comment-id:869704022 --> @crossworth commented on GitHub (Jun 28, 2021): Hi, do you want to handle the code further only on error or even on success? If it's only on error, you could probably create a custom error with the data you need, return it and type assert on your middleware. If you want even with success, I think a better approach would be using `context.WithValue`, maybe changing `Task` to have the context on it. ```go // Task represents a unit of work to be performed. type Task struct { // Type indicates the type of task to be performed. Type string // Payload holds data needed to perform the task. Payload Payload Context context.Context } ``` With this approach we could easily do: ```go // inside the task handler // t *asynq.Task t.Context = context.WithValue(t.Context, "myKey", "myValue") ``` and retrieve the value in the middleware. ```go err := h.ProcessTask(t) if err != nil { return err } fmt.Println("Val: ", t.Context.Value("myKey")) ``` I think this way is more idiomatic and similar of how the http requests works.
Author
Owner

@sujit-baniya commented on GitHub (Jun 28, 2021):

@crossworth I would want to handle in both cases and apply condition on middleware accordingly

<!-- gh-comment-id:869711018 --> @sujit-baniya commented on GitHub (Jun 28, 2021): @crossworth I would want to handle in both cases and apply condition on middleware accordingly
Author
Owner

@hibiken commented on GitHub (Jun 29, 2021):

@sujit-baniya Thank you for opening a feature request!
And @crossworth thank you so much for helping answering questions for various feature requests/bugs 🙏

As @crossworth mentioned, you should use a custom error type if you want to distinguish different error scenarios.

For successful case, both of the suggestion are valid but requires API changes.
I'm going to be working on adding a support for workflow and I think we'll implement some mechanism to store results for that feature.

In the meantime, I think you can use task ID and store the result in DB or Redis.
Example:

func myHandler(ctx context.Context, t *asynq.Task) error {
    res, err := DoStuff(ctx, t)
    if err != nil {
        return err
    }
    taskID, _ := asynq.GetTaskID(ctx)
    _, err := WriteToDB(taskID, res)
    return err 
}

and in the middleware, you can read the persisted result.

err := h.ProcessTask(ctx, t)
if err != nil {
	return err
}
taskID, _ := asynq.GetTaskID(ctx)
res, err := ReadFromDB(taskID)
<!-- gh-comment-id:870732712 --> @hibiken commented on GitHub (Jun 29, 2021): @sujit-baniya Thank you for opening a feature request! And @crossworth thank you so much for helping answering questions for various feature requests/bugs 🙏 As @crossworth mentioned, you should use a custom error type if you want to distinguish different error scenarios. For successful case, both of the suggestion are valid but requires API changes. I'm going to be working on adding a support for workflow and I think we'll implement some mechanism to store results for that feature. In the meantime, I think you can use task ID and store the result in DB or Redis. Example: ```go func myHandler(ctx context.Context, t *asynq.Task) error { res, err := DoStuff(ctx, t) if err != nil { return err } taskID, _ := asynq.GetTaskID(ctx) _, err := WriteToDB(taskID, res) return err } ``` and in the middleware, you can read the persisted result. ```go err := h.ProcessTask(ctx, t) if err != nil { return err } taskID, _ := asynq.GetTaskID(ctx) res, err := ReadFromDB(taskID) ```
Author
Owner

@hibiken commented on GitHub (Nov 7, 2021):

This can now be done through ResultWriter since version 0.19!

<!-- gh-comment-id:962635062 --> @hibiken commented on GitHub (Nov 7, 2021): This can now be done through `ResultWriter` since version 0.19!
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
starred/asynq#2138
No description provided.