Yabetsu2018
Yabetsu2018

Reputation: 133

How to Handle and get response from goroutines in golang

I have go routines called in getPosition, getStatus and rerouting for my drone currently I am invoking go GetStatus in my main function which this function has go func() which handles the event for streaming grpc and sse.

currently this is my code and i tried

func GetPositionContext(ctx context.Context, uav pb.UAVControllerClient, uavID *pb.UAVID, projectID string) {
    log.Printf("getPosition start")

    stream, err := uav.GetPosition(ctx)

    if err != nil {
        fmt.Printf("ERROR getPosition:%s", err.Error())
    }

    streamID, eventName := EventsSubscribe(projectID, uavID.Aircraft, "get_position")

    position := make(chan models.DronePosition)

    // 受信ループ開始
    go func() {
        fmt.Print("start getPosition loop")
        for {
            msg, err := stream.Recv() // msg UAVPosition
            if err == io.EOF {
                // read done.
                fmt.Print("start getPosition loop closed")
                close(position)
                return
            }
            if err != nil {
                log.Fatalf("Failed to receive getPosition : %v", err)
                close(position)
                return
            }
            // log.Printf("Position point[%s](%f, %f, %f) H:%f", uavID.Aircraft, msg.Latitude, msg.Longitude, msg.Altitude, msg.Heading)

            wayPoint := models.WaypointItem{
                Latitude:  msg.Latitude,
                Longitude: msg.Longitude,
                Altitude:  msg.Altitude,
                Heading:   msg.Heading,
            }

            dronePosition := models.DronePosition{
                Name:          uavID.Aircraft,
                ItemParameter: wayPoint,
            }

            // publish to eventgo
            publishNotif(dronePosition, streamID, eventName)
            return
        }
    }()

    startMsg := pb.UAVControllerPositionRequest{
        UavID:       uavID,
        Instruction: true,
        Interval:    2,
    }

    fmt.Print("send getPosition start")

    if err := stream.Send(&startMsg); err != nil {
        log.Fatalf("Failed to send getPosition: %v", err)
    }

    <-position

    stream.CloseSend()
    fmt.Print("end of getPosition")
}

this is the part where i call this function

go utils.GetPosition(ctx, uavService, &uavID, projectID)

I want to get the return values from go func like if everything works fine in the grpc server and no problem i should return 200 success and 500 if something failed.

for {
        time.Sleep(time.Duration(60) * time.Second)
    }

after the call i have this code which it should return every 10s a success

return c.JSON(500, failed or pass)

I expect something to return to the ui if go routines is success or failed when streaming part is working for the response not to be pending or else other api call will not work.

Upvotes: 3

Views: 2382

Answers (2)

Nathan Surfus
Nathan Surfus

Reputation: 129

If you want to know if there was an error when processing the go func() inside GetPositionContext then you could do something like below. I modified all errors to bubble up rather than call log.Fatal. On a side note it might be wise to defer stream.CloseSend(), but i'm missing context and it's outside the scope of the question.

I did my best to understand the question, but if I missed something, let me know!

The code has been modified to bubble up errors within GetPositionContext directly rather than call log.Fatal. Additionally, if there is an error within the go func() it will get sent back to GetPositionContext through a channel and then returned to the caller.

func GetPositionContext(ctx context.Context, uav pb.UAVControllerClient, uavID *pb.UAVID, projectID string) error {
    log.Printf("getPosition start")

    stream, err := uav.GetPosition(ctx)

    if err != nil {
        return fmt.Errorf("ERROR getPosition: %v", err.Error())
    }

    streamID, eventName := EventsSubscribe(projectID, uavID.Aircraft, "get_position")

    errC := make(chan error)

    // 受信ループ開始
    go func() {
        fmt.Print("start getPosition loop")
        for {
            msg, err := stream.Recv() // msg UAVPosition
            if err == io.EOF {
                // read done.
                fmt.Print("start getPosition loop closed")
                close(errC)
                return
            }
            if err != nil {
                errC <- fmt.Errorf("Failed to receive getPosition : %v", err)
                return
            }
            // log.Printf("Position point[%s](%f, %f, %f) H:%f", uavID.Aircraft, msg.Latitude, msg.Longitude, msg.Altitude, msg.Heading)

            wayPoint := models.WaypointItem{
                Latitude:  msg.Latitude,
                Longitude: msg.Longitude,
                Altitude:  msg.Altitude,
                Heading:   msg.Heading,
            }

            dronePosition := models.DronePosition{
                Name:          uavID.Aircraft,
                ItemParameter: wayPoint,
            }

            // publish to eventgo
            publishNotif(dronePosition, streamID, eventName)

            // Did you mean to return here? The for loop will only ever execute one time.
            // If you didn't mean to return, then remove this close
            close(errC)
            return
        }
    }()

    startMsg := pb.UAVControllerPositionRequest{
        UavID:       uavID,
        Instruction: true,
        Interval:    2,
    }

    fmt.Print("send getPosition start")

    if err := stream.Send(&startMsg); err != nil {
        return fmt.Errorf("Failed to send getPosition: %v", err)
    }

    err = <- errC

    stream.CloseSend()
    fmt.Print("end of getPosition")
    return err
}

If you want to call this function asynchronously, but still want to know if there was an error then you could do the following.

errC := make(chan error)
go func() {
  errC <- GetPositionContext(..........)
}()
// Do some other stuff here
// Until you need the result
err := <- errC


// ... and Eventually when you want to return the success or failure as JSON
if err != nil {
  return c.JSON(500, failed)
}
return c.JSON(500, pass)

Upvotes: 1

Sam White
Sam White

Reputation: 135

I would suggest setting up a channel to receive events from the go routine. In the calling function you can listen for these events (which can contain any information you want) in a loop until the go routine closes itself, signigying failure.

Upvotes: 0

Related Questions