Asynchronous Calls

From NSB App Studio
Revision as of 09:47, 19 December 2013 by James (talk | contribs)
Jump to navigation Jump to search

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 = escape("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 escape() 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

The Eval() function causes requestComplete() to be called.

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: