HttpClient GetAsync working in a VB.Net Console application but not in Windows Forms - vb.net

Works as expected in the console application (I converted it from a C# YouTube tutorial for reasons I won't bore you with), but hangs with no exception thrown in the desktop app when calling GetAsync.
`Imports System
Imports System.Net.Http
Module Moduke1
Sub Main()
Dim strContent As Task(Of String) = GetRequest("http://www.google.com.pk")
Console.WriteLine(strContent.Result)
Console.ReadKey()
End Sub
Async Function GetRequest(url As String) As Task(Of String)
Using client As New HttpClient()
Using response As HttpResponseMessage = Await client.GetAsync(url)
Using content As HttpContent = response.Content
Dim myContent As String = Await content.ReadAsStringAsync()
Return myContent
End Using
End Using
End Using
End Function
End Module`
That works, but the following does not. Probably a rookie error, although I'm not really a rookie - never used System.Net.Http until now and I've been all round the houses with this one.
The following hangs at the call to GetAsync...
`Imports System
Imports System.Net.Http
Public Class HTTP_Test_One
Public Sub HTTP_Test_One_Load(sender As Object, e As EventArgs) Handles MyBase.Load
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim strContent As Task(Of String) = GetRequest("http://www.google.com.pk")
txtResults.Text = strContent.Result
End Sub
Async Function GetRequest(url As String) As Task(Of String)
Using client As New HttpClient()
Using response As HttpResponseMessage = Await client.GetAsync(url)
Using content As HttpContent = response.Content
Dim myContent As String = Await content.ReadAsStringAsync()
Return myContent
End Using
End Using
End Using
End Function
End Class`

I'm not 100% on this, and suspect I may get corrected by people more in the know than I, maybe even the why. Looks to me that awaiting the Result of your task is jamming up, why not in the Console app, my guess is because it doesn't have the overhead of UI thread, or being triggered by an event. Just by reworking your button event handler, I at least get the desired response.
Private Async Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim strContent As String = Await GetRequest("http://www.google.com.pk")
txtResults.Text = strContent
End Sub
Note I've changed the Event to Async, which means I can just await the response from GetRequest() rather than looking at the task result

Blocking on async code in the UI thread is likely to lead to a deadlock. This has been discussed in a number of async-related resources; my go-to is the series of posts by Stephen Cleary, and he discusses this specific issue in https://blog.stephencleary.com/2012/07/dont-block-on-async-code.html with some additional detail on why blocking can lead to a deadlock.
The right way to do this is to make your event handler Async and then Await the result within it, i.e.
Private Async Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim content = Await GetRequest("http://www.google.com.pk")
txtResults.Text = content
End Sub
Note that Async Sub is generally recommended against due to issues with processing errors, but event handlers are the exception---Async Sub is designed specifically for UI event handlers.

Related

How to speed up Binance coin price parsing?

I'm trying to create a bot that place multiples orders on Binance and I need a fast coin price parsing.
I'm parsing the code using this code
Dim price as decimal
Private Async Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
Timer1.Stop()
Dim downloadTasks As New List(Of Task(Of String))
Dim dogebusd = wc1.DownloadStringTaskAsync("https://api.binance.com/api/v1/ticker/price?symbol=DOGEBUSD")
downloadTasks.Add(dogebusd)
Await Task.WhenAll(downloadTasks)
Dim d = JsonConvert.DeserializeObject(Of Dictionary(Of String, String))(dogebusd.Result)
Dim PREZZO As Decimal = CDec(Val((d("price")).ToString))
price = CDec((PREZZO))
Timer1.Start()
End Sub
but when the price is dumping or pumping really fast, even a 10 ms or 100ms timer tick is not that efficient.
I wonder if this is the fastest way possible or if I can improve the code.
Thanks
following Jimi suggestions:
Dim price As Decimal
Private ReadOnly _timer As New PeriodicTimer(TimeSpan.FromSeconds(1))
Private Async Sub Timer1_Tick(sender As Object, e As EventArgs) Handles _timer.Tick
timer1.stop
Dim dogebusdTask = HttpClient.GetStringAsync("https://api.binance.com/api/v1/ticker/price?symbol=DOGEBUSD")
Await dogebusdTask
Dim d = JsonConvert.DeserializeObject(Of Dictionary(Of String, String))(dogebusdTask.Result)
If Decimal.TryParse(d("price"), NumberStyles.Number, CultureInfo.InvariantCulture, price) Then
_timer.Start()
End If
End Sub
An example, using the .NET 6+ awaitable PeriodicTimer and System.Text.Json to parse the resulting stream returned by HttpClient's GetStreamAsync()
The PeriodicTimer can be quite efficient in contexts like this, since it tries to keep up with the interval you have provided. Since the internal procedure takes time to execute - the HttpClient.GetStreamAsync() takes an undetermined amount of time to execute and return a result - the Timer keeps track of the actual elapsed time and tries to keep ticking steadily (unless the procedure takes more than the specified Interval, then you skip a beat, of course)
Note: as usual, when dealing with Tasks that accept a CancellationToken, you should run this code without a Debugger (CTRL+F5) or configure the Debugger to not halt on TaskCanceledException or OperationCanceledException, otherwise it handles the (ignorable) exception before you do
Imports System.Globalization
Imports System.Net.Http
Imports System.Text.Json
Imports System.Threading
Private Shared ReadOnly m_Client As New HttpClient()
Private timerCts As CancellationTokenSource = Nothing
Private m_PeriodicTimer As PeriodicTimer = Nothing
Private m_PeriodicTimerInterval As Integer = 500
Private currentPrice As Decimal = 0.0D
Private Async Function StartPriceLookupTimer(token As CancellationToken, timerInterval As Integer) As Task
If token.IsCancellationRequested Then Return
Dim lookupAddress = "https://api.binance.com/api/v1/ticker/price?symbol=DOGEBUSD"
m_PeriodicTimer = New PeriodicTimer(TimeSpan.FromMilliseconds(timerInterval))
Try
While Await m_PeriodicTimer.WaitForNextTickAsync(token)
Dim jsonStream = Await m_Client.GetStreamAsync(lookupAddress, token)
Try
Dim prop = (Await JsonDocument.ParseAsync(jsonStream, Nothing, token)).RootElement.GetProperty("price")
Dim strPrice = prop.GetString()
If Decimal.TryParse(strPrice, CultureInfo.InvariantCulture, currentPrice) Then
' Do whatever you need to do with the parsed value
' E.g., assign it to a TextBox for presentation,
' using the current Culture format, since strPrice contains the original format
[Some TextBox].Text = currentPrice.ToString() ' <= UI Thread here
End If
Catch knfex As KeyNotFoundException
Debug.WriteLine("The JSON property was not found")
Catch tcex As TaskCanceledException
Debug.WriteLine("The lookup procedure was canceled")
End Try
End While
Catch tcex As TaskCanceledException
Debug.WriteLine("The lookup procedure was canceled")
End Try
End Function
Private Sub StopPriceLookupTimer()
timerCts?.Cancel()
m_PeriodicTimer?.Dispose()
timerCts?.Dispose()
timerCts = Nothing
End Sub
How to start this lookup procedure, calling the StartPriceLookupTimer() method?
You could use a Button, making its Click handler async:
Private Async Sub SomeButton_Click(sender As Object, e As EventArgs) Handles SomeButton.Click
If timerCts Is Nothing Then
timerCts = New CancellationTokenSource()
Await StartPriceLookupTimer(timerCts.Token, m_PeriodicTimerInterval)
Else
Debug.WriteLine("Lookup Timer already started")
End If
End Sub
Or make the Form's Shown Handler / OnShown() method override async and start the procedure when the Form is first presented:
Protected Overrides Async Sub OnShown(e As EventArgs)
MyBase.OnShown(e)
timerCts = New CancellationTokenSource()
Await StartPriceLookupTimer(timerCts.Token, m_PeriodicTimerInterval)
End Sub
To stop the lookup procedure, call the StopPriceLookupTimer() method when needed, e.g., using another Button or in the Form.Closed handler / OnFormClosed() override

How to get a result from a asynchronous task

I'm a beginner using async in VB.NET. I read online help but some things aren't clear.
I try to use tweetinvi library
I got this:
Namespace tweet_invi
Class twitter_call
Public Shared Async Function twitter_get_user_info_from_id(id As Long) As Task
Dim userClient = New TwitterClient(ConfigurationManager.AppSettings("consumerKey"), ConfigurationManager.AppSettings("consumerSecret"), ConfigurationManager.AppSettings("accessToken"), ConfigurationManager.AppSettings("accessTokenSecret"))
Dim tweetinviUser = Await userClient.Users.GetUserAsync(id)
Dim description As String = tweetinviUser.Description
End Function
End Class
End Namespace
And the module from where i would launch this async function
Private Sub Button3_Click(sender As Object, e As EventArgs) Handles Button3.Click
Dim toto As Long = 1311275527223812096
Dim result = tweet_invi.twitter_call.twitter_get_user_info_from_id(toto)
End Sub
My issue: result is a task. How do i have to get the value of description?
You can see it in the code you posted. The second line of that method does it. You use the Await operator to await the completion of the Task.
That said, there is no result to get anyway. If you have a synchronous Sub then that becomes an asynchronous Function that returns a Task. In both cases, there is no actual value to get out of the method. As such, awaiting such a method doesn't return anything. If you have a synchronous Function with a return type of T then that becomes an asynchronous Function that returns a Task(Of T). Awaiting that gives you a result of type T.
If you had these methods:
Private Sub DoSomething()
'...
End Sub
Private Function GetSomething() As SomeType
'...
End Function
then you'd call them like this:
DoSomething()
Dim someValue As SomeType = GetSomething()
If you had these methods:
Private Async Function DoSomethingAsync() As Task
'...
End Function
Private Async Function GetSomethingAsync() As Task(Of SomeType)
'...
End Function
then you'd call them like this:
Await DoSomethingAsync()
Dim someValue As SomeType = Await GetSomethingAsync()
VB actually does support Async Sub but the ONLY time you should ever us it is for event handlers, which MUST be declared Sub, i.e. you cannot handle an event with a Function. Also, any method in which you want to use the Await operator must be declared Async. Together, that means that you must declare the Click event handler of your Button as Async Sub and then you can await an asynchronous method in it:
Private Async Sub Button3_Click(sender As Object, e As EventArgs) Handles Button3.Click
Dim toto As Long = 1311275527223812096
Await tweet_invi.twitter_call.twitter_get_user_info_from_id(toto)
End Sub
With regards to the code you posted, that twitter_get_user_info_from_id method is useless. It declares and sets some local variables but does nothing with the data it gets. I suspect that that method should be like this:
Namespace tweet_invi
Class twitter_call
Public Shared Async Function twitter_get_user_info_from_id(id As Long) As Task(Of String)
Dim userClient = New TwitterClient(ConfigurationManager.AppSettings("consumerKey"), ConfigurationManager.AppSettings("consumerSecret"), ConfigurationManager.AppSettings("accessToken"), ConfigurationManager.AppSettings("accessTokenSecret"))
Dim tweetinviUser = Await userClient.Users.GetUserAsync(id)
Dim description As String = tweetinviUser.Description
Return description
End Function
End Class
End Namespace
and then you would call it like this:
Private Async Sub Button3_Click(sender As Object, e As EventArgs) Handles Button3.Click
Dim toto As Long = 1311275527223812096
Dim userInfo = Await tweet_invi.twitter_call.twitter_get_user_info_from_id(toto)
'...
End Sub

VB.Net BeginGetResponse much faster than using Await GetResponseAsync?

I am trying to start using tasks, but I wanted to compare the speed difference when using a standard HttpWebRequest.BeginGetResponse.
From what I have found, it is taking ~600ms to send and complete 100 requests to example.com using BeginGetResponse
However, using Await GetResponseAsync is taking 5x that. Around 3000ms. In production, that really matters alot to me when scaled up. Am I doing something wrong, or is Await GetResponseAsync inherently slower than BeginGetResponse?
Imports System.Net
Public Class Form1
Private sw As New Stopwatch
Private respCounter As Integer
Private iterations As Integer = 100
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
sw.Start()
For i = 1 To iterations
Dim req As HttpWebRequest = HttpWebRequest.Create("http://example.com")
Dim state As New RequestState
state.req = req
req.BeginGetResponse(AddressOf respCallback, state)
Next
End Sub
Private Sub respCallback(ar As IAsyncResult)
Dim state As RequestState = ar.AsyncState
state.resp = state.req.EndGetResponse(ar)
state.respStream = state.resp.GetResponseStream
state.respStream.BeginRead(state.buffer, 0, 1024, AddressOf readCallback, state)
End Sub
Private Sub readCallback(ar As IAsyncResult)
Dim state As RequestState = ar.AsyncState
Dim read As Integer = state.respStream.EndRead(ar)
If read > 0 Then
state.respBody += System.Text.ASCIIEncoding.ASCII.GetString(state.buffer, 0, read)
state.respStream.BeginRead(state.buffer, 0, 1024, AddressOf readCallback, state)
Else
state.Dispose()
respCounter += 1
If respCounter = iterations Then
respCounter = 0
sw.Stop()
Debug.WriteLine(sw.ElapsedMilliseconds)
sw.Reset()
End If
End If
End Sub
Private Async Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
sw.Start()
For i = 1 To iterations
Dim req As HttpWebRequest = HttpWebRequest.Create("http://example.com")
Using resp As WebResponse = Await req.GetResponseAsync
Using sr As New IO.StreamReader(resp.GetResponseStream)
Dim respBody As String = Await sr.ReadToEndAsync
End Using
End Using
respCounter += 1
If respCounter = iterations Then
respCounter = 0
sw.Stop()
Debug.WriteLine(sw.ElapsedMilliseconds)
sw.Reset()
End If
Next
MsgBox("Execution!")
End Sub
End Class
Public Class RequestState
Implements IDisposable
Public req As HttpWebRequest
Public resp As HttpWebResponse
Public respStream As IO.Stream
Public buffer(1024) As Byte
Public respBody As String
#Region "IDisposable Support"
Private disposedValue As Boolean ' To detect redundant calls
' IDisposable
Protected Overridable Sub Dispose(disposing As Boolean)
If Not disposedValue Then
If disposing Then
' TODO: dispose managed state (managed objects).
respStream.Close()
respStream.Dispose()
resp.Close()
End If
' TODO: free unmanaged resources (unmanaged objects) and override Finalize() below.
' TODO: set large fields to null.
End If
disposedValue = True
End Sub
' TODO: override Finalize() only if Dispose(disposing As Boolean) above has code to free unmanaged resources.
'Protected Overrides Sub Finalize()
' ' Do not change this code. Put cleanup code in Dispose(disposing As Boolean) above.
' Dispose(False)
' MyBase.Finalize()
'End Sub
' This code added by Visual Basic to correctly implement the disposable pattern.
Public Sub Dispose() Implements IDisposable.Dispose
' Do not change this code. Put cleanup code in Dispose(disposing As Boolean) above.
Dispose(True)
' TODO: uncomment the following line if Finalize() is overridden above.
' GC.SuppressFinalize(Me)
End Sub
#End Region
End Class
is Await GetResponseAsync inherently slower than BeginGetResponse?
It's difficult to address your specific performance concern without a good Minimal, Complete, and Verifiable code example. That said…
It seems to me that you're comparing apples and oranges here. First and foremost, there's a major difference in the implementations. In your BeginGetResponse() version, you initiate all of the requests concurrently, so assuming the web server will tolerate it, they complete in parallel. In your GetResponseAsync() version, you only initiate a new request after the previous one completes.
This serialization will necessarily slow everything down.
Beyond that, the BeginGetResponse() version performs all of its work in the IOCP thread pool, while the GetResponseAsync() version uses the single UI thread to handle completion of I/O events. The UI thread is a bottleneck, both because it can only do one thing at a time, and because you have to wait for it to be available from performing other tasks before it can move on to dealing with the I/O completions (a variation on the "can only do one thing at a time" issue).
In addition to that, you also have to deal with the latency involved in the message loop that dequeues the asynchronous completions for execution in the UI thread.
It wouldn't surprise me at all to find that the GetResponseAsync() approach is slower, when used in the way you're using it.
If you want better performance from it, you should probably use ConfigureAwait(false) in your async calls. Of course, this assumes you can otherwise minimize the interaction with the UI thread (e.g. the processing of the results does not actually need direct posting back to the UI thread). But doing so will tell the framework to not bother marshaling completions back to the UI thread. At least in the code you posted, this would be safe, as you don't actually interact with UI objects in the async event handler method.
All that said, when I changed your code so that it would run the GetResponseAsync() version concurrently, I found that at least with the web server I tested with, it worked just as fast as the BeginGetResponse() version. It was able to complete 100 iterations in just over 10 seconds in both cases.
Private Async Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
sw.Start()
Dim tasks As List(Of Task(Of String)) = New List(Of Task(Of String))
For i = 1 To iterations
Dim req As HttpWebRequest = HttpWebRequest.Create("http://example.com/")
tasks.Add(ReadResponse(req))
Next
Await Task.WhenAll(tasks)
sw.Stop()
Debug.WriteLine(sw.ElapsedMilliseconds)
sw.Reset()
MsgBox("Execution!")
End Sub
Private Async Function ReadResponse(req As HttpWebRequest) As Task(Of String)
Using resp As WebResponse = Await req.GetResponseAsync
Using sr As New IO.StreamReader(resp.GetResponseStream)
Dim respBody As String = Await sr.ReadToEndAsync
Return respBody
End Using
End Using
End Function
It's possible with a faster web server, you might start to run into the UI-thread-as-a-bottleneck issue, but I would say the primary difference is likely just that the two implementations really aren't even logically the same.

ASP.NET (VB.NET 4.5) Calling Async WCF Method

I have been reading for days, and I can't quite figure out what I am supposed to do here. I am actually a C# developer and programming in VB.NET can be a little confusing at times. That aside, I am trying to implement Async calls from a WCF in a new project that I am creating here. I've tried it a few different ways with results, but I want to make sure what I am doing is proper.
From what I have read, you should never return a void in an async method, so I am trying my hardest to avoid it. All of that aside, I guess I just want to know if this is a valid way to go about building this page. It will be calling more than one method from the WCF as I build it out.
Public Class _DefaultReservation
Inherits System.Web.UI.Page
Dim wcfReservation As WCFReservation.WDReservationClient
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
SetupPage()
End Sub
Private Async Sub SetupPage()
wcfReservation = DirectCast(Master, LoggedInMaster).wcfReservation
Dim resData As String = Await wcfReservation.GetDataAsync(123)
Response.Write(resData)
End Sub
End Class
I guess what is confusing is if I put that code inside of an async function and return the task, I would have to mark the page_load handler as async as well. It doesn't seem right doing it this way? By doing that it seems like an async function is calling an async function. But the way I am doing it here, my async function returns void, and that is supposed to be avoided. I can post an example of the other way too if needed. Thank you!!
Edit: Does this work better?
Imports System.Threading.Tasks
Public Class _DefaultReservation
Inherits System.Web.UI.Page
Dim wcfReservation As WCFReservation.WDReservationClient
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
SetupPage()
End Sub
Private Async Sub SetupPage()
wcfReservation = DirectCast(Master, LoggedInMaster).wcfReservation
Dim getDataResult = Await GetDataAsync()
Response.Write(getDataResult)
End Sub
Private Function GetDataAsync() As Task(Of String)
Return wcfReservation.GetDataAsync(123)
End Function
End Class
EDIT 3:
Imports System.Threading.Tasks
Public Class _DefaultReservation
Inherits System.Web.UI.Page
Dim wcfReservation As WCFReservation.WDReservationClient
Protected Async Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
wcfReservation = DirectCast(Master, LoggedInMaster).wcfReservation
Dim result As String = Await wcfReservation.GetDataAsync(1234)
Response.Write(result)
End Sub
End Class
It is true you should avoid async void. The exception for this guideline is when you have async event handlers.
Such as Page_Load.
For more information about this guideline, see my MSDN article on Best Practices in Asynchronous Programming.
It doesn't seem right doing it this way? By doing that it seems like an async function is calling an async function.
That's perfectly correct. Async code will "grow" through your code base. The correct solution is to make SetupPage a Task-returning function and await it in Page_Load (which is an async void/Sub).
Edit:
Imports System.Threading.Tasks
Public Class _DefaultReservation Inherits System.Web.UI.Page
Dim wcfReservation As WCFReservation.WDReservationClient
Protected Async Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
Dim getDataResult = Await GetDataAsync()
Response.Write(getDataResult)
End Sub
Public Function GetDataAsync() As Task(Of String)
wcfReservation = DirectCast(Master, LoggedInMaster).wcfReservation
Return wcfReservation.GetDataAsync(123)
End Function
End Class
As luck would have it, I just posted about Async Sub yesterday. Long story short, in your case if you use Async Sub, control will revert to the calling method as soon as the await is encountered. As a result, your page_Load handler will end before the SetupPage is complete. If you want to have Page_Load wait until SetupPage has asynchronously completed, you need to change the SetupPage to be a function returning Task and then Await SetupPage in Page_Load (causing Page_Load) to be Async.
Async Sub is valid on event handlers. Lucian discusses this at some length in his recent Async Patterns post. You may also want to check out the Async talk from ASP.Net Conf last year for special considerations on using Async with ASP.Net/WCF.

Normal v Async calls to a service

I have a wcf service reference configured on a client application. It provides a whole series of functions to both retrieve and send data to a web based database. As an example:
Function errorCodesGetAll(ByVal uname As String, ByVal pword As String) As String
and
Function errorCodesGetAllAsync(ByVal uname As String, ByVal pword As String) As System.Threading.Tasks.Task(Of String)
I know that I can populate a rich text box with the first function by using the following code:
RichTextBox1.Text = getCountryList
Private Function getCountryList() As String
Dim svc As New ServiceReference2.ERSAPIServiceClient
svc.Open
Dim str As String = svc.errorCodesGetAll(username, password)
svc.Close()
Return str
End Function
As WCF is still a very new area to me I'm wondering how I would populate the same rich text box but this time using the Async variant of the errorCodesGetAll function?
Thanks for any advice or general pointers as to how the async variants are best used.
Your service will expose a "completed" event as well as the async method, you need to handle that event.
Open the service, wire up the event and call the async method
Private Sub GetCodes()
Dim svc As New ServiceReference2.ERSAPIServiceClient
AddHandler ServiceReference2.errorCodesGetAllCompleted, AddressOf errorCodesGetAllCompletedhandler
ServiceReference2.errorCodesGetAllAsync()
ServiceReference2.Close()
End Sub
Handle the event. This will get called when the service returns. (normally I would not add the "handler" to the end of the method and name it exactly the same as the event, but I thought it might help distinguish the event and the handler)
Private Sub errorCodesGetAllCompletedHandler(ByVal sender As Object, ByVal e As ServiceReference2.errorCodesGetAllEventArgs)
If Not e.Result Is Nothing Then
textbox.text = e.Result
End If
End Sub
Calling the async version of the method is interesting when you're on the UI thread (e.g., on the Click event handler for a button in your form), since that won't "freeze" the UI by blocking the thread, waiting for the networking call to complete.
Since you get the *Async method which returns a Task<T> result, I assume you're using the .NET Framework 4.5. If this is the case, you can take advantage of the Async / Await keywords to call the asynchronous version in a fairly simple way, while still preventing the UI thread from being blocked.
Private Async Sub Button_Click(ByVal sender as Object, ByVal e as EventArgs)
RichTextBox1.Text = Await getCountryList
End Sub
Private Async Function getCountryList() As Task(Of String)
Dim svc As New ServiceReference2.ERSAPIServiceClient
svc.Open
Dim str As String = Await svc.errorCodesGetAllAsync(username, password)
svc.Close()
Return str
End Function