0

I'm using webforms and VB.net. I am calling Stripe to perform an ACH transaction. I have a standalone program that works, however when I put the code in my production application, the flow is not what I'm expecting.

I'm reading a gridview which contains customer IDs of records which need to be billed via Stripe. I have a button (btnPost) which starts the process. That button calls an Async Sub which loops through the gridview. For each row I call a "Post_ACHAsync" subroutine, which is also Async. Within the "Post_ACHAsync" subroutine is the call to Stripe, with an "Await", which returns a Task.

The problem is that, when the call to Stripe is done, it goes back to the For/Each loop instead of proceeding with code. Why is it doing that?

Here's the code:


Protected Sub btnPost_Click(sender As Object, e As EventArgs) Handles btnPost.Click

    PostTransactions()
    PopulatePendingTrxs()

End Sub


    Protected Async Sub PostTransactions()
        For Each row As GridViewRow In gvTrxs.Rows
            Select Case row.Cells(2).Text.ToUpper()
                Case "B"
                    '--- Bank Account (ACH)
                    Post_ACHAsync([parameters])
                Case "C"
                    '--- Credit Card
                    Post_CC(row)
                Case Else

            End Select
        Next
    End Sub


  Private Async Sub Post_ACHAsync([parameters])          
    dim oM as new BillingMaster()
    
      '--- call Stripe
          Try
              Dim stripeService As New StripeService()
              Dim chargeId As String = Await stripeService.ProcessAchTransferAsync([parameters])
              ' Success!
              oM.Status = "W"     'Withdrawal
              oM.SuccessOrFailure = "S"
              oM.StripeWithdrawalID = chargeId
              oM.StripeWithdrawalMsg = "Success"
              colErrors = oM.SaveRecord(oM)
          Catch ex As Exception
              OKtoContinue = False
              colErrors.Add(ex.Message)
          End Try

      If Not OKtoContinue Then
          oM.PostGUID = strPostGUID
          oM.Status = "F"
          oM.SuccessOrFailure = "F"
          oM.StripeWithdrawalID = ""
          oM.WithdrawalDate = DateTime.Now
          oM.StripeWithdrawalMsg = SplitErrors(colErrors)
          colErrors = oM.SaveRecord(oM)
      End If

      oM = Nothing

  End Sub   
  
  
  Public Async Function ProcessAchTransferAsync([parameters]) As Task(Of String)
    Try
        
        '--- do Stripe stuff here

        Return charge.Id
        
    Catch ex As StripeException
        ' Log the error
        System.Diagnostics.Debug.WriteLine($"Stripe Error: {If(ex.StripeError?.Message, ex.Message)}")
        Throw New Exception($"Payment processing error: {If(ex.StripeError?.Message, ex.Message)}")
        
    Catch ex As Exception
        ' Log the error
        System.Diagnostics.Debug.WriteLine($"General Error: {ex.Message}")
        Throw New Exception("An unexpected error occurred while processing your payment. Please try again.")
        
    End Try
    
End Function

After the Await stripeService.ProcessAchTransferAsync line, the flow goes back to the For/Each loop; it doesn't continue with the "Success" (or Catch) code.

Do I need both subroutines to be Async? Should there be another "Await" somewhere?

4
  • 2
    you need to await the calls Commented Mar 30 at 18:27
  • 5
    btnPost_Click needs to be marked async, then await the methods there; it's OK for event handlers to be async void (it's handled internally). The other methods not so much. Change them from async void to async Task. Await the async calls Commented Mar 30 at 18:33
  • Remember that when you use Async, it's Async all the way down. Every step in the chain needs to go through an Await. Commented Mar 31 at 15:05
  • You really shouldn't use a UI control to store data - instead, store the data somewhere else, for example a DataTable, and display that data with a UI control, for example with the GridView.DataBind Method. Trying to use a UI control for processing data will result in slower processing. Commented Mar 31 at 18:57

1 Answer 1

0

Because Post_ACHAsync is not awaited, the execution flow immediately returns to PostTransactions when the Await line is reached:

Dim chargeId As String = Await stripeService.ProcessAchTransferAsync([parameters])

Take a look at the diagram in the Task asynchronous programming model article:

enter image description here

The same is happening with your code where control is returned to the calling method when the Await statement is reached.

You should await the Post_ACHAsync call:

Protected Async Sub PostTransactions()
    For Each row As GridViewRow In gvTrxs.Rows
        Select Case row.Cells(2).Text.ToUpper()
            Case "B"
                '--- Bank Account (ACH)
                Await Post_ACHAsync([parameters])
            Case "C"
                '--- Credit Card
                Await Post_CC(row)
        End Select
    Next
End Sub

You should also Await PostTransactions in your button event handler:

Protected Async Sub btnPost_Click(sender As Object, e As EventArgs) Handles btnPost.Click

    Await PostTransactions()
    PopulatePendingTrxs()

End Sub
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.