RxJava Flowable.Interval backpressure when flatmap with single - kotlin

I'm having a scenario where I need to periodically call an API to check for a result. I'm using Flowable.interval to create an interval function which calls the API.
However, I'm having trouble with backpressure. In my example below, a new single is created on each tick in the interval. The desired effect is to only call the API if a call is not already in progress
Flowable.interval(1, 1, TimeUnit.SECONDS).flatMap {
System.out.println("Delay $it")
//simulates API call
Single.just(1L).doAfterSuccess {
System.out.println("NEW SINGLE!!!")
}.delay(4, TimeUnit.SECONDS).doAfterSuccess {
System.out.println("SINGLE SUCCESS!!!")
}.toFlowable()
}.subscribeOn(Schedulers.io()).observeOn(Schedulers.computation()).blockingFirst()
I can solve this using a filter variable like so:
var filter = true
Flowable.interval(1, 1, TimeUnit.SECONDS).filter {
filter
}.flatMap {
System.out.println("Delay $it")
Single.just(1L).doOnSubscribe {
filter = true
}.doAfterSuccess {
System.out.println("NEW SINGLE!!!")
}.delay(4, TimeUnit.SECONDS).doAfterSuccess {
System.out.println("SINGLE!!!")
filter = true
}.toFlowable()
}.subscribeOn(Schedulers.io()).observeOn(Schedulers.computation()).blockingFirst()
But it seems like a hacky solution. I've tired applying onBackPressureDrop after the interval function, but it has no effect.
Any suggestions?

You have to constrain flatMap as well:
Flowable.interval(1, 1, TimeUnit.SECONDS)
.onBackpressureDrop()
.flatMapSingle({
System.out.println("Delay $it")
//simulates API call
Single.just(1L).doAfterSuccess {
System.out.println("NEW SINGLE!!!")
}.delay(4, TimeUnit.SECONDS).doAfterSuccess {
System.out.println("SINGLE SUCCESS!!!")
}
}, false, 1) // <----------------------------------------------------------
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.computation())
.subscribe()

Related

How to parse json array with Retrofit and RxJava

I have problem parsing array of objects using Retrofit + RxJava
JSON contains only this array
{
"files": [
{
"id": 10,
"notificationId": 15,
"name": "cats.ncs",
"dateTime": "2019-01-07T17:34:45"
}
]
}
retrotfit service
#GET("/api/FileApi/files")
fun files(): Observable<FilesResponse>
where FilesResponse is
data class FilesResponse(
#SerializedName("files")
var files: List<FileElement>
)
and FileElement
data class FileElement(
#SerializedName("id")
var id: Long,
#SerializedName("notificationId")
var notificationId: Long,
#SerializedName("name")
var name: String,
#SerializedName("dateTime")
var dateTime: String
)
when I run it I get always
the return type of CallObjectMethodA does not match
io.reactivex.Observable ApiService.files()
So how do I parse JSON containing only an array?
Try using RxJava2 Adapter
Integration
implementation 'com.squareup.retrofit2:adapter-rxjava2:{retrofit_version}'
Retrofit client setup
new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create()) //option 1
.addCallAdapterFactory(RxJava2CallAdapterFactory.createWithScheduler(Schedulers.newThread())) //option 2
.build();
Few notes
First of, you might need to show us how you call ApiService.files()
What could possibly went wrong
How it is called
What could be the possible solution
Invoke the method like this
ApiService
.files()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({
// Handle the values here
},{
// Handle the error here
},{
// Handle the completion here (optional)
})
Not like this
ApiServices.files()
nor
var fileResponse = ApiServices.files()
Ensure the you have setup your retrofit builder well, as mentioned above
RxJava2CallAdapterFactory
GsonConverterFactory
Other than that, without the code on how you call it, we won't be able to help you in an in-depth manner.

RxJava Maybe: Any neat way to handle Empty case?

I am stuck at this problem, which should be fairly simple. I need subscriber to execute a code block when the Maybe has completed as an Empty Maybe. I found that
we can pass default Maybe value or use switchIfEmpty but I feel both are hacky.
Also there is a subscribe function which takes onComplete function (along with handlers for other two events), but onComplete does not take any argument which can be used to find if maybe was completed empty.
Another way could be Maybe.isEmpty.blockingGet(), but it is dirty too.
I have tried following (Kotlin Syntax):-
fun <T> Maybe<T>.subscribeWithEmptyHandler(onSuccess: (T) -> Unit, onError: (Throwable) -> Unit, onEmpty: () -> Unit) {
this.isEmpty.subscribe({ if (it) onEmpty() }, { onError(it) })
this.subscribe({ onSuccess(it) }, { onError(it) })
}
But as expected it is running subscription twice, tested here:-
Maybe.create<Int> {
println("subscribing")
//Remove line below to create Empty Maybe
it.onSuccess(5)
it.onComplete()
}
.subscribeWithEmptyHandler({println("success")},{println("error")},{println("empty")})
Could somebody please suggest neater way to solve this?
Use Maybe.doOnEvent (java example):
Maybe
.empty()
.doOnEvent((value, error)-> {
if (value==null && error == null) {
System.out.println("empty!");
}})
.subscribe();
There is a solution using flatMap
return Maybe.just<String>(smth)
.flatMap(
Function {
Maybe.just(it) // onSuccess
},
Function {
Maybe.error(it) // onError
},
Callable { // onComplete
Maybe.just("Empty")
}
)
Or
return Maybe.just<String>(smth)
.flatMap(
{
Maybe.just<String>(it) // onSuccess
},
{
Maybe.error(it) // onError
},
{
Maybe.just("Empty") // onComplete
}
)
I did the following which is neater than any I wrote in the question:-
fun <T> Maybe<T>.subscribeWithEmptyHandler(onSuccess: (T) -> Unit, onError: (Throwable) -> Unit, onEmpty: () -> Unit) {
this.toSingle()
.subscribe(
{ onSuccess(it) },
{ if (it is NoSuchElementException) onEmpty() else onError(it) }
)
}
Here it subscribes only once and doesn't involve creating new default values. Still not sure if this is the best way.

Synchronous call to Rest API in polymer 2.0

Can any body tell me as how to make synchronous calls to Rest services in polymer 2.0.
I am trying calculate client total balance which I can do only after receiving response from all below three Rest services.
Is there any way to synchronize them using some api e.g. Promise or anyother
<iron-ajax id="balanceAjax" url="/balances" last-response="{{res1}}"></iron-ajax>
<iron-ajax id="currencyAjax" url="/currencies" last-response="{{res2}}"></iron-ajax>
<iron-ajax id="rateAjax" url="/rates" last-response="{{res3}}"></iron-ajax>
You can have same observer function for all the three properties (res1,2 and 3) and in observer if all of them have some value then proceed and calculate the value.
res1: {
observer: 'responseChanged'
},
res2: {
observer: 'responseChanged'
}
res3: {
observer: 'responseChanged'
},
...
responseChanged: function() {
if(this.res1 && this.res2 && this.res3) {
//calculate total balance
}
}
You can also have one single observer instead of three
res1: {
},
res2: {
}
res3: {
},
...
observers: [responseChanged(res1, res2, res3)],
responseChanged: function() {
if(this.res1 && this.res2 && this.res3) {
//calculate total balance
}
}
Alternately, you can also use on-response listener of iron-ajax.
Lastly, if your res1 ,2 and 3 are expected to have value even before the api call you can set some booleans in on-response listener of each ajax and have observer on those booleans instead.

Complete function in WinJS.UI.Pages.IPageControlMembers method

How can I complete function in init before call ready method. My code:
WinJS.Namespace.define("Data", {
source: ""
});
var page = WinJS.UI.Pages.define("/html/page.html", {
init: function (element, options) {
createDataSoucre();
},
ready: function () {
document.getElementById("result").innerHTML = Data.source;
}
});
function createDataSoucre() {
//blah blah (calculate thousands of calculations)
Data.source = result;
}
When I run, page doesn't render "result" tag. I try use promises but it doesn't work for me:
init: function (element, options) {
return new WinJS.Promise.as(createDataSoucre());
}
Thanks for your time.
I tried your code in a simple test project as follows:
(function () {
"use strict";
WinJS.Namespace.define("Data", {
source: ""
});
function createDataSource() {
Data.source = "<ul><li>Item 1</li><li>Item2</li><li>Item3</li></ul>";
}
WinJS.UI.Pages.define("/pages/home/home.html", {
init: function (element, options) {
createDataSource();
},
ready: function (element, options) {
document.getElementById("result").innerHTML = Data.source;
}
});
})();
Everything works as expected, with the bullet list appearing on the page.
However, I believe you're asking how to make your createDataSource function asynchronous in itself, so that it can do your "thousands of calculations" off the UI thread and produce a promise that the init function can return. This way, the page loading process will wait upon the completion of those calculations.
What's needed here is to use new WinJS.Promise rather than WinJS.Promise.as. The as method just wraps a value in a promise so that the value is send to any completed handler you attach, but doesn't create an async function automatically. That's something you have to do.
Let me provide an example from Appendix A of my free ebook, Programming Windows Store Apps with HTML, CSS, and JavaScript, Second Edition, where Chapter 3 and Appendix A go into all the details about promises. Here's a function that does a long series of calculations using setImmediate to break up the work on the UI thread. (You could also use web workers to put the work on another thread, or use a WinRT component--but I'll leave it to my book to talk about those subjects, which is in Chapter 18 in the section "Implementing Asynchronous Methods").
function calculateIntegerSum(max, step) {
//The WinJS.Promise constructor's argument is a function that receives
//dispatchers for completed, error, and progress cases.
return new WinJS.Promise(function (completeDispatch, errorDispatch, progressDispatch) {
var sum = 0;
function iterate(args) {
for (var i = args.start; i < args.end; i++) {
sum += i;
};
if (i >= max) {
//Complete--dispatch results to completed handlers
Data.source = "Sum is <em>" + sum + "</em>";
completeDispatch(sum);
} else {
//Dispatch intermediate results to progress handlers
progressDispatch(sum);
setImmediate(iterate, { start: args.end, end: Math.min(args.end + step, max) });
}
}
setImmediate(iterate, { start: 0, end: Math.min(step, max) });
});
}
You can do something similar for your own process. The key here is that when your process is complete, you have to call the completeDispatch function that's given to your initializer. In the code above I'm also putting the result into Data.source.
With such a method, your createDataSource function can look like this:
function createDataSource() {
return calculateIntegerSum(100000, 2);
}
Because calculateIntegerSum returns a promise and is implemented to be async, createDataSource will return a promise that you can return from init:
init: function (element, options) {
return createDataSource();
},
I tried this out in a project and it works just fine, with the page loading waiting upon the calculations to complete.

Vows: Testing Asynchronous Interleaving

Is there a methodology to test (potential) interleaving of asynchronous functions with vows?
For example:
// Topic portion
var user = new User('jacob')
user.set('email,'foo#bar.com')
user.save() // a
user.set('email',derp#cherp.com')
user.save() // b
user.refresh(this.callback) // Reload from database
// Callback
assert.equals(user.email,'derp#cherp.com')
There is could be a race condition between the two saves. When writing my tests I want to ensure that my API is ensuring that b finishes last (and that we have the correct final value for the email). With the way that's it written, the test will pass coincidentally some of the time.
Heres the example from the vows docs:
The nested contexts act as nested callbacks and pass the return arguments to the next context.
Docs: http://vowsjs.org/
{ topic: function () {
fs.stat('~/FILE', this.callback);
},
'after a successful `fs.stat`': {
topic: function (stat) {
fs.open('~/FILE', "r", stat.mode, this.callback);
},
'after a successful `fs.open`': {
topic: function (fd, stat) {
fs.read(fd, stat.size, 0, "utf8", this.callback);
},
'we can `fs.read` to get the file contents': function (data) {
assert.isString (data);
}
}
}
}