B4J Tutorial [BANano] Working with promises

alwaysbusy

Expert
Licensed User
BANano 2.19+ has a new object: BANanoPromise. It allows you to easily use Javascript Promises.

A promise can be useful e.g. if you want to upload/download files, which can take some time to do. It is comparable with B4Js Wait For.

This is probably the easiest to understand explanation of a Promise I could find on the interweb:

---------------------------------------------------
A Promise in short:

"Imagine you are a kid. Your mom promises you that she'll get you a new phone next week."

You don't know if you will get that phone until next week. Your mom can either really buy you a brand new phone, or stand you up and withhold the phone if she is not happy :(.

That is a promise. A promise has 3 states. They are:
  1. Pending: You don't know if you will get that phone
  2. Fulfilled: Mom is happy, she buys you a brand new phone
  3. Rejected: Your mom is happy, she withholds the phone
----------------------------------------------------
From: https://scotch.io/tutorials/javascript-promises-for-dummies
It is a very good read if you are new to Promises.

The source code of this tutorial, with comments, can be found in the zip (the Promises folder).

BASIC STRUCTURE:
B4X:
' the promise itself
Dim promise As BANanoPromise
' the results from the promise
Dim Result As Map
Dim Error As String
                 
' call a method 
promise.CallSub(Module, "MethodName", Array(Param1, Param2, ...))

' if success         
promise.Then(Result)
' or in case you use Wait or Sleep methods in the success branch
promise.ThenWait(Result)
' if an error
promise.Else(Error)
' or in case you use Wait or Sleep methods in the error branch
promise.ElseWait(Error)
' closing a promise
promise.End
So for example letting the user upload files would look like this:
B4X:
' get all the files selected from the input #fu
Dim UploadedFiles() As String = BANano.GetElement("#fu").GetField("files").Result
     
' make a promise that will show "Done" if ALL file all uploaded
Dim promise As BANanoPromise
' the results from the promise
Dim Result As Map
Dim Error As String
                 
' call the UploadAll method 
promise.CallSub(Me, "UploadAllFiles", Array(UploadedFiles))
         
' when it is done, we can use whatever the UploadAll has returned in its BANano.ReturnThen call 
promise.Then(Result)
    ' let's show all our urls
    For i = 0 To Result.Size - 1
       Log(Result.GetKeyAt(i) & "=" & Result.GetValueAt(i))
    Next 
promise.Else(Error)
    Log("Error: " & Error)     
promise.End
Methods called using Promise.CallSub (+all methods called in it and further on) do not use the normal Return! Instead, one can use one of these two methods:
B4X:
' returns to the above promise.Then (or .promise.ThenWait)
BANano.ReturnThen(result)
' returns to the above promise.Else (or .primise.ElseWait)
BANano.ReturnElse(error)
Promises can be chained. For example in the demo, once we have all the upload files, we do a http request to get some json:
B4X:
' make a promise that will show "Done" if ALL file all uploaded
Dim promise As BANanoPromise
' the results from the promise
Dim Result As Map
Dim Error As String
                 
' call the UploadAll method 
promise.CallSub(Me, "UploadAllFiles", Array(UploadedFiles))
         
' when it is done, we can use whatever the UploadAll has returned in its BANano.ReturnThen call 
' we use the ThenWait here because we use ...Wait functions or in this case a Sleep
promise.ThenWait(Result)
       Log("Done, going to sleep for 2 seconds just to demonstrate ThenWait()...")
       Sleep(2000)
       Log("Printing result after 2 seconds")
       ' let's show all our urls
       For i = 0 To Result.Size - 1
           Log(Result.GetKeyAt(i) & "=" & Result.GetValueAt(i))
       Next 
promise.Then(Null)
       ' now how about next we make a HTTP request after we got all files             
       DoHTTP("assets/product.json")
promise.Else(Error)
       Log("Error: " & Error)     
promise.End
Such a chained promise.Then() can be a promise in itself:
B4X:
public Sub DoHTTP(Url As String) As String 'ignore
   ' again we make a new promise
   Dim promise As BANanoPromise
   ' some vars to hold our results
   Dim Error As String
   Dim json As String
 
   ' call the http request
   promise.CallSub(Me, "DoHTTPForUrl", Array(Url))
 
   promise.Then(json)
       '  we got it!
       Dim jsonElem As BANanoElement = BANano.GetElement("#json")
       jsonElem.SetText(json)
   promise.Else(Error)
       ' whoops, something happened...
       Dim jsonElem As BANanoElement = BANano.GetElement("#json")
       jsonElem.SetText(Error)
   promise.end 
End Sub
The demo has documented almost every line, so check it out. What it does:

1. Allows the user to upload some files

2. When all downloads are finished, make a http request to download some json

Note: for the full effect of the demo, use a real Web server (or use the Chrome Web server plugin). If you just open the html file from disk, you'll get something like this:



In case it runs on a Web server, the json is returned:



Alwaysbusy
 
Last edited:

Harris

Well-Known Member
Licensed User
ImpressionableOfficialJackal-size_restricted.gif


You're a very bad man, Jerry....:)

For i = 0 To Result.Size - 1
Log(Result.GetKeyAt(i) & "=" & Result.GetValueAt(i))
Next
The recommended way, if this works in BANano?

B4X:
For Each k As String In Result.Keys

   Log( "Key: "&k&"   - Value: "&Result.Get(k) )

Next
I am very guilty of this as well, but am revising all A and i code... (not much concerned in J and ABM - but I should be).
Forgive me - couldn't resist.
 
Top