Asynchronous Calls
Using Ajax Asynchronously
In our last Ajax discussion, we discovered that it could take half a second for an Ajax call to complete (actually, due to network latency, it could even be a lot more than that.) How do we do Ajax calls and not make our app look slow?
Easy. We send the Ajax message off, then go do other stuff. When the response comes back, we respond to it. This is called Asynchronous operation.
We do this by adding the name of the function to be called on completion as the 4th parameter of our Ajax call:
req=Ajax("ajax.php/?myText=" + txtSend.value, "", "", done)
Our callback function is called 'done'. After we execute the Ajax call, the program goes on to the next statement in the program immediately. It doesn't wait for any response. We can update the screen, or wait for the user to do something else. When the reply comes back, our 'done' function will be called:
Dim req Function btnAjax_onclick() req=Ajax("ajax.php/?myText=" + txtSend.value, "", "", done) txtResponse.value="Waiting for result." End Function Function done() If req.readyState<>4 Then Exit Function 'ignore progress reports If req.status=200 Then 'success txtResponse.value=req.responseText Else 'failure txtResponse.value="Error: " & req.err.message End If End Function
'done' will actually be called several times with progress reports on the call. We only care if it completes, so we wait for req.readyState to be 4. The rest of our function works just like when we were not asynchronous.
One of the uses of this technique that we see every day is to return partial results on a search field (as in Google). Each time a character is entered, it is sent to the server for a list of possible matches. As the results come back, the window is updated. Entering characters in the field is unaffected by the server communications that are going on at the same time.
Check out the AjaxAsync app in the Samples to see this in action.
Using JSONP to get a Stock Quote
Working with Ajax, we have run into the Same Origin Policy over and over again. In this post, we'll show you another way to get around it. The Same Origin Policy restricts you from loading files from a server not your own. There is an exception: JavaScript (.js) files are allowed. There are sites on the web where you can request information and have it passed back in JSONP format. The "P" stands for padding: JSONP is JSON with padding. The results are returned as a function call, with the results as parameters. That function can then be called in your program.
Let's use this to get a stock quote from Yahoo. There are 3 parts to this: first, we have to make the request. Next, we handle the return. Finally, we execute the function.
Make the request
Dim YQI, URL, callback YQI = encodeURIComponent("select * from yahoo.finance.quotes where symbol in ('AAPL','GOOG','MSFT')") callback="requestComplete" URL = "http://query.yahooapis.com/v1/public/yql?q=" & YQI & _ "&format=json&env=http://datatables.org/alltables.env&callback=" & callback loadScript(URL)
Yahoo! has a very powerful API to look up all kinds of data. To make it easier, they have set up an interface using SQL like statements called YQI. In our code above, we start by creating our YQI request. The encodeURIComponent() function translates special characters and spaces so they can be sent in our Ajax call.
URL is then composed of our YQI request with the rest of the boilerplate that is needed. The most interesting part is the &callback part. It names the function that the return values will be wrapped in.
Having prepared everything, we can inject the script.
Inject the script
Yahoo! is going to return some code: it will be a call to the function we specified in callback, with the parameters all filled in. The following code will insert the code returned by Yahoo! into our program and execute it:
Function loadScript(URL) Dim head, script head=document.getElementsByTagName("head")[0] script=document.createElement("script") script.src=URL script.async=True head.appendChild(script) End Function
We do the usual checking to see if the call is complete. If it is, we will have gotten a text string back that looks something like this:
requestComplete({"query": {"count":3,"created":"2012-03-20T11:34:00Z","lang":"en-US","results": {"quote":...);
This is a valid function call, so when it is run, the function is executed.
Execute the function
Function requestComplete(data) Dim quotes, i quotes=data.query.results.quote TextArea1.value="" For i=0 To Len(quotes)-1 TextArea1.value=TextArea1.value+quotes[i].symbol & quotes[i].Ask & vbCRLF Next End Function
The data that comes back is a fairly complex structure with a lot of data in it. You can copy it to the NotePad to see everything that is there. For now, we are just interested in the asking price of the stock. The results are returned in an array, with one element per stock.
Cool things you can get on the web
There are many web services that work in a similar fashion. One of the tip offs is if the calling string has an &callback argument. If it does, it probably can be used with this method.
Some of these services are free or limited, while others require an API key that you must obtain from the service.
Some places to go for services:
- The master list is here: Programmable Web
- Lots of stuff: Yahoo!
- Weather: Wunderground
- Elevation: Geonames
- Geographic Info: Geonames