I want to call a function (synchronously) and then use its return value as an initial emission (subsequently chaining some other operators on the resulting observable).
I want to invoke this function during subscription, so I can't just use Observable.of(() => getSomeValue()). I've seen bindCallback (previously fromCallback) but I don't think it can be used for this task (correct me if I'm wrong). I've seen start static operator in v4 docs but apparently it is not implemented in v5 (and no indication that its on the way). RxJava also has fromCallable operator that does exactly that afaik.
Only way I could think of is like this:
Observable.create((observer: Observer<void>) => {
let val = getSomeValue();
observer.next(val);
observer.complete();
})
which I think does just that. But this just seems so complicated for a simple thing that should probably have been like Observable.fromFunction(() => getSomeValue()) And what if I want to run it asynchronously, like start operator does? How can I do this in the current version of RxJS?
I tend to avoid any explicit use of Observable.create where ever possible, because generally it is a source of bugs to have to manage not just your event emission but also your teardown logic.
You can use Observable.defer instead. It accepts a function that returns an Observable or an Observable-like thing (read: Promise, Array, Iterators). So if you have a function that returns an async thing it is as easy as:
Observable.defer(() => doSomethingAsync());
If you want this to work with a synchronous result then do:
Observable.defer(() => Observable.of(doSomethingSync()));
Note: That like create this will rerun the function on each subscription. This is different then say the result of Observable.bindCallback which stores the function call result without re-executing the function. So if you need that sort of behavior you will need to use the appropriate multicasting operator.
An implementation of a fromFunction$ that I used in my project:
function fromFunction$<T>(factory: () => T): Observable<T> {
return Observable.create((observer: Subscriber<T>) => {
try {
observer.next(factory());
observer.complete();
} catch (error) {
observer.error(error);
}
});
}
Used like:
fromFunction$(() => 0).subscribe((value) => console.log(`Value is '${value}'`), null, () => console.log('Completed'));
fromFunction$(() => [1, 2, 3]).subscribe((value) => console.log(`Value is '${value}'`), null, () => console.log('Completed'));
fromFunction$(() => { throw 'Something' }).subscribe(null, (error) => console.error(`Error: ${error}`));
Gives:
Value is '0'
Completed
Value is '1,2,3'
Completed
Error: Something
Until such implementation exists.
Actually I think the best option is using Observable.create because it's the most universal solution for both synchronous and asynchronous initial values.
If you're sure you'll use a synchronous function you can use startWith() operator (this makes sence only if return value from getSomeValue() should be the same for all Observers).
Using Observable.bindCallback as a source Observable is of course doable however I personally recommend to avoid it because it makes your code very hard to understand and it's usually not necessary because you can use just Observable.create.
Related
I need to pass three data to one function from three different APIs:
this.service.service1().subscribe( res1 => {
this.service.service1().subscribe( res2 => {
this.service.service1().subscribe( res3 => {
this.funcA(res1, res2, res3);
});
});
});
Is it a good practice to subscribe inside a subscribe?
The correct way is to compose the various observables in some manner then subscribe to the overall flow — how you compose them will depend on your exact requirements.
If you can do them all in parallel:
forkJoin(
this.service.service1(), this.service.service2(), this.service.service3()
).subscribe((res) => {
this.funcA(res[0], res[1], res[2]);
});
If each depends on the result of the previous, you can use mergeMap (formerly known as flatMap) or switchMap:
this.service.service1().pipe(
mergeMap((res1) => this.service.service2(res1)),
mergeMap((res2) => this.service.service3(res2))
).subscribe((res3) => {
// Do something with res3.
});
... and so on. There are many operators to compose observables to cover lots of different scenarios.
Though all of the above help provide solutions to this particular problem none of them seem to address the obvious underlying problem here, specifically:
Is it good way to call subscribe inside subscribe?
tl;dr
No it is not good to call a subscribe inside a subscribe.
Why?
Well because this is not how functional programming is supposed to work. You're not thinking functionally you're thinking procedurally. This isn't necessarily a problem per se, but the whole point of using rxjs (and other reactive programming extensions) is to write functional code.
I'm not going into all the details on what functional programming is but essentially the point of functional programming is to treat data as streams. Streams that are manipulated by functions, and consumed by subscribers. As soon as you add a subscribe inside another subscribe your manipulating data inside a consumer (not inside a stream). So your functional stream is now broken. This prevents other consumers from utilising that stream further down stream in your code. So you've turned your functional stream into a procedure.
Image source, above and more information on pure functional programming here.
You can use forkJoin to combine the Observables into a single value Observable
forkJoin(
this.service.service1(),
this.service.service2(),
this.service.service3()
).pipe(
map(([res1, res2, res3 ]) => {
this.funcA(res1, res2, res3);
})
If the calls can be resolved in parallel you could use forkJoin, like this:
joinedServiceCalls() {
return forkJoin(this.service1(), this.service2(), this.service3());
}
And then subscribe to that method.
https://www.learnrxjs.io/operators/combination/forkjoin.html
Looks strange, I would go this way because it looks cleaner:
async myFunction () {
//...
const res1 = await this.service.service1().toPromise();
const res2 = await this.service.service2().toPromise();
const res3 = await this.service.service3().toPromise();
this.funcA(res1, res2, res3);
//...
}
EDIT
or to do it in parallel
async myFunction () {
//...
let res1;
let res2;
let res3;
[res1,res2,res3] = await Promise.all([this.service.service1().toPromise(),
this.service.service2().toPromise(),
this.service.service3().toPromise()]);
this.funcA(res1, res2, res3);
//...
}
You can use the zip RxJs operator, and then in this case you will only use just one subscribe.
You can then call your function inside that subscribe because all the results are available.
Observable.zip(
this.service.service1(),
this.service.service1(),
this.service.service1()
).subscribe([res1, res2, res3]) {
this.funcA(res1, res2, res3);
}
as mentioned, forkjoin is a good solution, but it emit completed calls only. If these are values that are going to be emitted repeatedly, use I would combineLatest.
I'm working with observables and the flatMap operator, I wrote a method which makes and API call and returns an observable with an array of objects.
Basically what I need is to get that array of objects and process each object, after all items are processed. I want to chain the result to make an extra API call with another method that I wrote.
The following code does what I need:
this.apiService.getInformation('api-query', null).first().flatMap((apiData) => {
return apiData;
}).subscribe((dataObject) => {
this.processService.processFirstCall(dataObject);
}, null, () => {
this.apiService.getInformation('another-query', null).first().subscribe((anotherQueryData) => {
this.processService.processSecondCall(anotherQueryData);
});
});
But this approach isn't optimal from my perspective, I would like to do chain those calls using flatMap but if I do the following:
this.apiService.getInformation('api-query', null).first().flatMap((apiData) => {
return apiData;
}).flatMap((dataObject) => {
this.processService.processFirstCall(dataObject);
return [dataObject];
}).flatMap((value) => {
return this.apiService.getInformation('another-api-query', null).first();
}).subscribe((value) => {
this.processService.processSecondCall(value);
});
The second API call executes once for each item on the apiData array of objects. I know I'm missing or misunderstanding something. But from the second answer of this thread Why do we need to use flatMap?, I think that the second flatMap should return the processed apiData, instead is returning each of the object items on that Array. I would appreciate the help.
Thank you.
What you want is the .do() operator, and not flatMap(). flatMap() is going to transform an event to another event, and in essence, chaining them. .do() just executes whatever you instruct it to do, to every emission in the events.
From your code, there are 2 asynchronous methods (calls to the api) , and 2 synchronous (processService). What you want to do is :
Call to the first API (async), wait for the results
Process the results (sync)
Call to the second API (async), wait for the results to come back
Process the results (sync)
Hence your code should be :
this.apiService.getInformation('api-query', null)//step1
.first()
.do((dataObject) => this.processFirstCall(dataObject))//step2
.flatMap(() => this.apiService.getInformation('another-api-query', null))//step3
.first()
.do(value => this.processService.processSecondCall(value))//step4
.subscribe((value) => {
console.log(value);
});
I wrote in comment the steps corresponding to the list above. I also cleaned up unnecessary operators (like your first flatMap is kinda redundant).
Also, in the event you want to transform your data, then you should use .map() instead of .do(). And I think that is the place where you are confused with .map() and .flatMap().
The issue I think your encountering is that flatmap should be applied to an observable or promise. in your second code example, you are returning data within the flatmap operator which is then passed to the following flatmap functions, whereas these should be returning observables.
For example:
this.apiService.getInformation('api-query', null).first()
.flatMap((dataObject) => {
return this.processService.processFirstCall(dataObject);
}).flatMap((value) => {
return this.apiService.getInformation('another-api-query', null)
}).subscribe((value) => {
this.processService.processSecondCall(value);
});
See this post for futher clarification.
What is the best way to determine if the subscriber has finished executing or better yet return something and catch it up-stream? For example:
this._subscriptions.push(this._client
.getCommandStream(this._command) // Returns an IObservable from a Subject stream
.subscribe(msg => {
// Do some processing maybe some promise stuff
http.request(url).then(
// some more stuff
);
});
What's the best know to determine that subscription has finished. I've implemented it as follows:
this._subscriptions.push(this._client
.getCommandStream(this._command)
.subscribe(msg => {
// Do some processing maybe some promise stuff
http.request(url).then(re => {
// some more stuff
msg.done()
}).catch(err => msg.done(err));
});
i.e. added a done method to the object being passed in to determine if this is finished. The issue with that is I'll have to call done in every promise or catch block and find that a little too exhaustive. Is there a cleaner and more automated way of doing this?
I think the examples I've given are not good enough. This implementation is using RX to build an internal messaging bus. The get command stream is actually returning a read-only channel (as an Observable) to get commands and process them. Now the processing could be a http request followed by many other things or just an if statement.
this._client
.getCommandStream(this._command) // Returns an IObservable from a Subject stream
.subscribe(msg => {
// Do some processing maybe some promise stuff
http.request(url).then({
// some more stuff
}).then({
// Here I wanna do some file io
if(x) {
file.read('path', (content) => {
msg.reply(content);
msg.done();
});
} else {
// Or maybe not do a file io or maybe even do some image processing
msg.reply("pong");
msg.done()
}
});
});
I feel like this is a fine usage of the Observable pattern as this is exactly a sequence of commands coming in and this logic would like to act on them. The question is notice msg.done() being called all over the place. I want to know what is the best way to limit that call and know when the entire thing is done. Another option is to wrap it all in a Promise but then again what's the difference between resolve or msg.done()?
Actually, making another asynchronous request inside subscribe() isn't recommended because it just makes things more complicated and using Rx in this way doesn't help you make your code more understandable.
Since you need to make a request to a remote service that returns a PRomise you can merge it into the chain:
this._subscriptions.push(this._client
.getCommandStream(this._command)
.concatMap(msg => http.request(url))
.subscribe(...)
Also the 3rd parameter to subscribe is a callback that is called when the source Observable completes.
You can also add your own teardown logic when the chain is being disposed. This is called after the complete callback in subscribe(...) is called:
const subscription = this._subscriptions.push(this._client
...
.subscribe(...)
subscription.add(() => doWhatever())
Btw, this is equivalent to using the finally() operator.
As per RxJs subscribe method documentation, the last Argument is completed function
var source = Rx.Observable.range(0, 3)
var subscription = source.subscribe(
function (x) {
console.log('Next: %s', x);
},
function (err) {
console.log('Error: %s', err);
},
function () {
console.log('Completed');
});
please refer this documentation
https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/core/operators/subscribe.md
There could be a simple answer for this but I've only ever had to use extension methods (are they even called that in JS?) in C#..
I have a 3rd party library that uses events. I need a function to be called after the event is called. I could do this easily via a promise but the 3rd party library was written before ES6 and does not have promises built in. Here's some sample code -
wRcon.on('disconnect', function() {
console.log('You have been Disconnected!')
})
Ideally I would like to be able to implement something like this -
wRcon.on('disconnect', function() {
console.log('You have been Disconnected!')
}).then(wRcon.reconnect())
So to summarize my question, how do I extend wRcon.on to allow for a promise (or some other callback method)?
Promises and events, while they seem similar on the surface, actually solve different problems in Javascript.
Promises provide a way to manage asynchronous actions where an outcome is expected, but you just don't know how long it's going to take.
Events provide a way to manage asynchronous actions where something might happen, but you don't know when (or even if) it will happen.
In Javascript there is no easy way to "interface" between the two.
But for the example you've given, there is an easy solution:
wRcon.on('disconnect', function() {
console.log('You have been Disconnected!')
wRcon.reconnect()
})
This is perhaps not as elegant as your solution, and breaks down if your intention is to append a longer chain of .then() handlers on the end, but it will get the job done.
You could wrap the connection in a way that would create a promise:
const promise = new Promise((resolve) => {
wRcon.on('disconnect', function() {
resolve();
})
}).then(wRcon.reconnect())
However, this does not seem like an ideal place to use Promises. Promises define a single flow of data, not a recurring event-driven way to deal with application state. This setup would only reconnect once after a disconnect.
Three/four options for you, with the third/fourth being my personal preference:
Replacing on
You could replace the on on the wRcon object:
const original_on = wRcon.on;
wRcon.on = function(...args) {
original_on.apply(this, args);
return Promise.resolve();
};
...which you could use almost as you showed (note the _ =>):
wRcon.on('disconnect', function() {
console.log('You have been Disconnected!');
}).then(_ => wRcon.reconnect());
...but I'd be quite leery of doing that, not least because other parts of wRcon may well call on and expect it to have a different return value (such as this, as on is frequently a chainable op).
Adding a new method (onAndAfter)
Or you could add a method to it:
const original_on = wRcon.on;
wRcon.onAndAfter = function(...args) {
wRcon.on.apply(this, args);
return Promise.resolve();
};
...then
wRcon.onAndAfter('disconnect', function() {
console.log('You have been Disconnected!');
}).then(_ => wRcon.reconnect());
...but I don't like to modify other API's objects like that.
Utility method (standalone, or on Function.prototype)
Instead, I think I'd give myself a utility function (which is not "thenable"):
const after = (f, callback) => {
return function(...args) {
const result = f.apply(this, args);
Promise.resolve().then(callback).catch(_ => undefined);
return result;
};
};
...then use it like this:
wRcon.on('disconnect', after(function() {
console.log('You have been Disconnected!');
}, _ => wRcon.reconnect()));
That creates a new function to pass to on which, when called, (ab)uses a Promise to schedule the callback (as a microtask for the end of the current macrotask; use setTimeout if you just want it to be a normal [macro]task instead).
You could make after something you add to Function.prototype, a bit like bind:
Object.defineProperty(Function.prototype, "after", {
value: function(callback) {
const f = this;
return function(...args) {
const result = f.apply(this, args);
Promise.resolve().then(callback).catch(_ => undefined);
return result;
};
}
});
...and then:
wRcon.on('disconnect', function() {
console.log('You have been Disconnected!');
}.after(_ => wRcon.reconnect()));
And yes, I'm aware of the irony of saying (on the one hand) "I don't like to modify other API's objects like that" and then showing an option modifying the root API of Function. ;-)
Background
I have a nodejs server running and I installed the promise package which follows the promise api specs.
Since I succeeded in making denodeify(fn, length) work, I am now in the process of chaining promises, but I am failing to grasp the main concepts.
What I tried
By reading the documentation example on the specification page, I reached the following code:
let Promise = require("promise");
let write = Promise.denodeify(jsonfile.writeFile);
let read = Promise.denodeify(jsonfile.readFile);
read("dataFile.txt").then( () => {
write("./testFile.txt", "hello_World", TABS_FORMATTING).then(console.log("file complete"));
});
Which is quite different from the examples I see, for example, in the Solutions Optimist tutorial:
loadDeparture( user )
.then( loadFlight )
.then( loadForecast );
Objective
My objective is to make my code as beautiful as the example I showed before, but I don't understand how I can make as concise as it is right now.
Question
1 - What changes do I need to perform in my code to achieve that level?
The given example uses named function to make it look as good as it can get, but that can be a bit redundant because then you're creating functions for every little thing in the chain. You must pick and choose when to use named functions over anonymous functions.
One thing you must also realize is that to chain promises you must return them.
So to make it a proper chain you must return the write method so it is passed down to the next step.
Also make sure that the catch() method is used at the bottom of every promise chain so that errors aren't silently swallowed.
Note that in the example here I'm using the ES2015 arrow functions to return the write() method as that makes it looks better(which seemed to be the purpose of your question).
let Promise = require("promise");
let write = Promise.denodeify(jsonfile.writeFile);
let read = Promise.denodeify(jsonfile.readFile);
read("dataFile.txt")
.then(() => write("./testFile.txt", "hello_World", TABS_FORMATTING))
.then(results => console.log("file complete", results))
.catch(error => console.error(err));
I'd recommend reading this article for some best practices.
Nesting promises kind of defeats the purpose because it creates pyramid code (just like callbacks).
The main concept that may be escaping you is that you can return inside a then and the returned value (can be a promise or a value) can then be accessed in a chained then:
read("dataFile.txt").then( () => {
return write("./testFile.txt", "hello_World", TABS_FORMATTING);
}).then( () => {
console.log("file complete");
});
Now, you can extract the functions:
function writeTheFile() {
return write("./testFile.txt", "hello_World", TABS_FORMATTING);
}
function consoleLog() {
console.log("file complete");
}
read("dataFile.txt")
.then(writeTheFile)
.then(consoleLog);