B4J Question [BANano] [SOLVED] How can I run a BANanoFetch with a timeout, lets say 5 seconds?

Solution
Timeout is determined by the browser (e.g. Chrome is 300 seconds, firefox = 90 seconds, etc...)

But we can make use of the PromiseRace capabilities to do this (there is indeed another solution with the experimental AbortController, but I personally prefer the Race way).

What you do is make two promises: your Fetch and a TimeOut. Then we let both promises race against each other. The first one wins!

For this example I wrote a Fetch3000 with a Sleep(3000) in it to emulate a slow fetch.
B4X:
Sub Process_Globals
      ' to hold our TimeOut object
      Public myTimeOut As Object
End Sub

' A new fetch method with a Timeout
Sub FetchTimeout(url As String, options As BANanoFetchOptions, timeout As Long) As BANanoPromise
    Dim prom...

Toky Olivier

Active Member
Licensed User
Longtime User
Hi,
I really love the directive [BANRAW]. I can do this:

Initially:
B4X:
    Dim response As BANanoFetchResponse
    Dim error As BANanoObject
    Dim data As BANanoJSONParser
 
    Dim options As BANanoFetchOptions
    options.Initialize
    options.Method = "GET"
    Dim fetch As BANanoFetch
    fetch.Initialize("http://worldtimeapi.org/api/timezone/Pacific/Noumea?rnd=" & DateTime.Now, options)
 
    fetch.Then(response)
        fetch.Return(response.json        )
    fetch.Then(data)
        BANano.Console.Log(data)
    fetch.Else(error)
        BANano.Console.Log("Error fetching")
    fetch.End

And to reply to your question (just you change the value of timeout field in options, if you put for example "5" instead of "1000", it will end up to an error) :
B4X:
    Dim response As BANanoFetchResponse
    Dim error As BANanoObject
    Dim data As BANanoJSONParser
   
    Dim options As BANanoFetchOptions
    options.Initialize
    options.Method = "GET"
    options.SetField("timeout", "1000")
    Dim fetch As BANanoFetch 'ignore
    
    Dim fetchWithTimeout As BANanoObject
    fetchWithTimeout.Initialize($"[BANRAW]
    async function fetchWithTimeout(resource, options = {}) {
      const { timeout = 8000 } = options;
      
      const controller = new AbortController();
      const id = setTimeout(() => controller.abort(), timeout);
      const response = await fetch(resource, {
        ...options,
        signal: controller.signal  
      });
      clearTimeout(id);
      return response;
    }
    "$)
    fetch = fetchWithTimeout.Execute(Array As Object("http://worldtimeapi.org/api/timezone/Pacific/Noumea?rnd=" & DateTime.Now, options))
        
    fetch.Then(response)
        fetch.Return(response.json        )
    fetch.Then(data)
        BANano.Console.Log(data)
    fetch.Else(error)
        BANano.Console.Log("Error fetching")
    fetch.End
(Code from this page: https://dmitripavlutin.com/timeout-fetch-request/)

It's just a fast solution but I think better solution is that @alwaysbusy inplement it in BANano as BANanoFetchWithTimeout for example...
 
Last edited:
Upvote 0

alwaysbusy

Expert
Licensed User
Longtime User
Timeout is determined by the browser (e.g. Chrome is 300 seconds, firefox = 90 seconds, etc...)

But we can make use of the PromiseRace capabilities to do this (there is indeed another solution with the experimental AbortController, but I personally prefer the Race way).

What you do is make two promises: your Fetch and a TimeOut. Then we let both promises race against each other. The first one wins!

For this example I wrote a Fetch3000 with a Sleep(3000) in it to emulate a slow fetch.
B4X:
Sub Process_Globals
      ' to hold our TimeOut object
      Public myTimeOut As Object
End Sub

' A new fetch method with a Timeout
Sub FetchTimeout(url As String, options As BANanoFetchOptions, timeout As Long) As BANanoPromise
    Dim prom As BANanoPromise = BANano.PromiseRace(Array(TimeOutAfter(timeout), Fetch3000(url, options)))
    Return prom
End Sub

#Region Fetch Promise
Sub Fetch3000(url As String, options As BANanoFetchOptions) As BANanoPromise
    Dim prom As BANanoPromise
    ' with ...Wait because we use a Sleep method in it to fake the delay
    prom.CallSub(Me, "FetchDelayed3000Wait", Array(url, options))    
    Return prom
End Sub

Sub FetchDelayed3000Wait(url As String, options As BANanoFetchOptions)
    Dim response As BANanoFetchResponse
    Dim error As Object
    
    Dim Fetch As BANanoFetch
    Fetch.Initialize(url, options)
    Fetch.ThenWait(response) ' wait because of the sleep method
        ' clear the TimeOut
        BANano.Window.ClearTimeout(myTimeOut)
        ' fake 3 seconds delay
        Sleep(3000)        
        If response.OK Then
            BANano.ReturnThen(response)
        Else ' some other error
            BANano.ReturnElse("Whoops, something else went wrong (file did not exist?)...")
        End If        
    Fetch.Else(error)
        ' clear the TimeOut        
        BANano.Window.ClearTimeout(myTimeOut)
        ' return the error
        BANano.ReturnElse(error)
    Fetch.end        
End Sub
#end Region

#Region TimeOut Promise and Helpers
Sub TimeOutAfter(timeout As Long) As BANanoPromise
    Dim prom As BANanoPromise
    prom.CallSub(Me, "DoTimeOutAfter", Array(timeout))
    Return prom
End Sub

Sub DoTimeOutAfter(timeout As Long)
    myTimeOut = BANano.Window.SetTimeout(BANano.CallBack(Me, "DoTimeout", Null),timeout)
End Sub

Sub DoTimeout() 'ignore    
     BANano.ReturnElse("Request Time-out")
End Sub
#End Region

Two tests: first where the timeout happens before the file is fetched
B4X:
Dim response As Object
Dim error As Object
' 2000 timeout < 3000 delayed fetch
Dim prom As BANanoPromise = FetchTimeout("favicon.ico", Null,2000)
prom.Then(response)
    Log(response)
prom.Else(error)
    Log(error) ' enters here because of the timeout or for example because the file did not exist
prom.end

Second one where the fetch is faster than the timeout
B4X:
Dim response As Object
Dim error As Object
' 5000 timeout > 3000 delayed fetch
Dim prom As BANanoPromise = FetchTimeout("favicon.ico", Null,5000)
prom.Then(response)
    Log(response) ' enters here
prom.Else(error)
    Log(error) ' enters here if something else went wrong, like file did not exist
prom.end

Alwaysbusy
 
Upvote 1
Solution
Top