I have a VBA function that is supposed to get some information from the user’s cell, make a POST request with that info, then print the response in the output cell.
It’s required that the user be able to make about 2000 requests at a time, so I thought to make the requests async to help improve performance.
As it stands right now, I have a function ConnectToAPI that makes the asynchronous request, then passes the response off to a callback function. The problem I’m having is that the data lives in the callback function, but I need it in the query function in order to return it.
Function Query(ID, quote, field) Application.Volatile Query = ConnectToAPI(ID) Some logic with parsed data from callback End Function Function ConnectToAPI(ID) Dim Request As New WebRequest Dim Client As New WebClient Client.BaseUrl = "http://www.endpoint.com" Dim Wrapper As New WebAsyncWrapper Dim Wrapper.Client = Client Dim Body As New Dictionary Body.Add "ID", ID Set Request.Body = Body Request.Method = HttpPost ConnectToAPI = Wrapper.ExecuteAsync Request, "CallbackFunction" End Function Function CallbackFunction Callback = Parsed Data End function
So ultimately in the query function, I want to write
Query = (Parsed Data From the Callback)
How can I pass the data from the callback back up to query?
It is important that the cell have the Query function in it. The data updates frequently, so we want clients to be able to calculate the workbook to get the newest data.
With what I currently have, my thought process is that the callback will pass the data back to ConnectToAPI, then that will be passed up to Query. However, my function returns 0 and I think this might be that the parsed data is not available once the function tries to return.
For reference, I am using the VBA-Web library
WebAsyncWrapper.ExecuteAsync has an optional parameter:
CallbackArgs. Use this parameter to pass back you an ID or a cell address.
ExecuteAsync has an example callback function that receives an Array of arguments.
Here is how you can get the information back to the function for processing.
Sub ConnectToAPI(ID As Variant, quote As Variant, field As Variant, CellAddress As Variant) Dim Request As New WebRequest Dim Client As New WebClient Client.BaseUrl = "http://www.endpoint.com" Dim Wrapper As New WebAsyncWrapper Dim Body As New Dictionary Body.Add "ID", ID Set Request.Body = Body Request.Method = HttpPost Set Wrapper.Client = Client Wrapper.ExecuteAsync Request, "Callback", Array(ID, CellAddress) End Sub Public Function Callback(Response As WebResponse, Args As Variant) Dim ID As Variant, CellAddress As Variant ID = Args(0) CellAddress = Args(1) With Worksheets("Web Requests") .Range(CellAddress).Value = Response .Range(CellAddress).Offset(0, 1).Value = ID End With End Function
Marks a user-defined function as volatile. A volatile function must be recalculated whenever calculation occurs in any cells on the worksheet. A nonvolatile function is recalculated only when the input variables change. This method has no effect if it’s not inside a user-defined function used to calculate a worksheet cell.
I would not recommend trying to have a UDF that can be used as a worksheet function to return the web-requests.
Application.Volatile will cause all 2000 queries to refresh every time a value is changed. When the first query updates all the other queries will refresh. This will cause an infinite loop and crash the application.
Function Query(ID, quote, field) Application.Volatile Query = ConnectToAPI(ID) Some logic with parsed data from callback End Function
Worksheet_Change event would give the users the ability to update the information without the problems associated with
Private Sub Worksheet_Change(ByVal Target As Range) If Not Intersect(Target, Columns("A")) Is Nothing Then If Target.Count = 1 Then Debug.Print Target.Value, Target.Address End If End If End Sub
I ended up populating a dictionary with the response values from the API call, then recursively calling the query function in the callback.
Query checks if the response value is in the dictionary, if it is, then it returns it. If not, it connects to the api, the callback puts the value in the dictionary, and also calls the query function again.