Start stream when another Observable emits its first value - ajax

I have two observables, one from key press events and another from ajax requests.
I want the second stream to start when the first stream emits its first value.
var input$ = Rx.Observable.fromEvent( input, 'keydown')
.debounceTime(500)
var countries$ = Rx.Observable.of('https://restcountries.eu/rest/v1/all')
.flatMap(url => $.get( url ))
.retryWhen( errors => {
return errors.scan( (sum, err) => {
if( sum === 2 )
{
throw err;
}
else{
return sum + 1;
}
}, 0).delay(1000)
})
I am using rxjs 5, I want the countries$ observable to start when the input$ observable emits its first value. I tried using skipUntil or debounce passing the input$ Observable but... no luck. I see the ajax request happening in the network tab before I start typing anything. Any idea how I can achieve what I want?

Use switchMap. This will switch from the source stream to the new stream whenever the source Observable emits
var url = 'https://restcountries.eu/rest/v1/all';
var countries$ = input$.switchMap(input => $.get(url))
.retryWhen(...
Note that if a new input arrives before the http request is completed, the old request will be cancelled and a new one issued. So switchMap is perfect if the http request made depends on the specific input (as in a search query)
I used a static URL because that's what your OP uses, but you could easily alter the URL to include a query parameter built from the input.
If you wish to run the http request just once regardless of what the input was, and do not want to cancel the old request when a new input arrives, use take to only read from the source once:
var countries$ = input$.take(1).switchMap(input => $.get(url))
.retryWhen(...

Related

Create single observable per event type & destroy on last unsubscribe

Imagine a stream of messages, each with associated user id. For each message that comes in, fetch the associated user information ('user-fetch' observable). These user-fetch observables will stay alive, and monitor any future changes for the target user.
Questions:
How to prevent duplicate 'user-fetch' observables from being created for a given user-id (and reuse the possibly already created observable)?
How to correctly cleanup all user-fetch observables for unsubscribe and/or complete?
Where I'm at:
I wasn't able to determine an existing operator or methodology to prevent duplicate observables, so I wrote an operator similar to switchMap. I don't love it. How is this done in practice?
If I can solve 1, I believe the solution to correct cleanup and reuse is refCount().
if I understood the problem correctly you have one stream that emits id-s and based on that stream events, another stream that receives some data related to the id from remote place (server).
The solution that I suggest is to create some kind of store to hold the cached data and upon receiving a message from the id stream to check it and return either the response from new request or the cached data.
/**
* callBack end mocks an http request
*/
let callBackEnd$ = id => {
customLog("___________________");
customLog("Calling the server for " + id);
customLog("___________________");
return of({ id: id, data: `Some data about ${id}` });
};
/**
* idStream$ mock the stream of id-s to be called trough http request
*/
let idStream$ = from([1, 2, 2, 3, 1, 5, 3, 4, 5]);
/**
* We use reqStore$ to cache the already retrieved data
*/
let reqStore$ = new BehaviorSubject([]);
/**
* 1. We subscribe to the message stream ( the stream that will tell us what to load )
*
* 2. With `latestFrom` we take the current store and check for any cached data, and return
* the cached data or the response of the new request
*
* 3. If the response of the `switchMap` doesn't exist in our store we add it.
*/
idStream$
.pipe(
tap(message => customLog(`Receiving command to retrieve : ${message}`)),
withLatestFrom(reqStore$),
switchMap(([e, store]) => {
let elementSaved = store.find(x => x.id === e);
return elementSaved ? of(elementSaved) : callBackEnd$(e);
}),
withLatestFrom(reqStore$),
tap(([response, store]) => {
if (!store.find(x => x.id === response.id)) {
reqStore$.next([...store, response]);
}
})
)
.subscribe(([currentResponse, currentStore]) => {
customLog("Receiving response for " + currentResponse.data);
});
Here is live demo at Codesandbox I hope that helps you out :)

PublishRequest<T> IBus extension equivalent in v5.x

With v5 PublishRequest extension was removed from the IBus interface.
We used the callback to handle multiple response types that could be returned from the consumer ( Faults, Validations, actual responses, etc )
What is the equivalent way of publishing a message and wiring up multiple response types ?
// Request/Response contracts, may also return validation failure or fault contract
Request<TMessage> request = await bus.PublishRequest<TMessage>( msg, context => {
context.Handle<TResponse>( value => ... );
context.Handle<TValidation>( value => ... );
context.Handle<Fault>( value => ... );
context.CorrelationId = ...
context.Headers.Set( ... );
});
await request.Task;
You can use the new syntax, which is much cleaner overall.
var client = Bus.CreateRequestClient<RegisterMember>();
var (registered, existing) =
await client.GetResponse<MemberRegistered, ExistingMemberFound>(
new RegisterMember() {MemberId = "Johnny5"});
This will return either of the two responses, and if a fault occurs, either will throw the faulted request exception.
You can also use a request handle to add headers, etc.
var client = Bus.CreateRequestClient<RegisterMember>();
var request = client.Create(new RegisterMember()
{MemberId = "Johnny5"});
// the request is also the send pipe configurator, so...
request.UseExecute(context => context.CorrelationId = someId);
var (registered, existing) =
await request.GetResponse<MemberRegistered, ExistingMemberFound>();
You can see a working test case in the future tests:
https://github.com/MassTransit/MassTransit/blob/develop/src/MassTransit.Futures.Tests/Request_Specs.cs#L170

Pause, upon resume give last paused value

I have a hot Observable fed by a socket. I can use the pausable to pause the socket feed. But once I 'unpause' the observable, I need to display the last values that the socket could have sent while the subscription was paused. I don't want to keep track of the last values the socket sends manually. How could this be pausible?
From the example in the documentation, see comments below:
var pauser = new Rx.Subject();
var source = Rx.Observable.fromEvent(document, 'mousemove').pausable(pauser);
var subscription = source.subscribe(
function (x) {
//somehow after pauser.onNext(true)...push the last socket value sent while this was paused...
console.log('Next: ' + x.toString());
},
function (err) {
console.log('Error: ' + err);
},
function () {
console.log('Completed');
});
// To begin the flow
pauser.onNext(true);
// To pause the flow at any point
pauser.onNext(false);
You don't even need pausable to do this. (Note as well that you tagged RxJS5 but pausable only exists in RxJS 4). You simply need to convert your pauser into a higher order Observable:
var source = Rx.Observable.fromEvent(document, 'mousemove')
// Always preserves the last value sent from the source so that
// new subscribers can receive it.
.publishReplay(1);
pauser
// Close old streams (also called flatMapLatest)
.switchMap(active =>
// If the stream is active return the source
// Otherwise return an empty Observable.
Rx.Observable.if(() => active, source, Rx.Observable.empty())
)
.subscribe(/**/)
//Make the stream go live
source.connect();

Should I create a .factory in order to share a variable's value to two ajax requests?

Using angularjs I have made two ajax json requests, the first request is retrieving channel's online / offline status and the second ajax json request is for the channel's info (logo, name, status etc).
I assigned the variable signal to 'data.stream' which is the online / offline status for channels to share signal between json requests. In Google Developer console I am receiving a value of null. I did some research here http://www.ng-newsletter.com/posts/beginner2expert-services.html and found that using a service might be a solution. I followed the directions but I'm still unable to get the scope between json request to recognize signal.
I read that rootscope could be used but it's not recommend, not sure how true that is because a novice using angular but I want start my angular journey by applying best practices.
Recap: Using Angular Ajax to make a jsonp request to twitch api, I am make two requests one to retrieve the online / offline status of channels in my streamers array, and the other ajax json request is to retrieve the channel's info, and I need the scope of the variable signal which has been assigned the value data.stream to be seen between ajax jsonp requests. Please let me know if this is a logical approach or if I'm "going around the world again".
Here is a plunker: plnkr.co/edit/6sb39NktX7CwfnQFxNNX
// creating a service for signal ?
app.factory('signal', function() {
var signal = {};
return signal;
})
// declaring my TwitchController and dependencies
app.controller('TwitchController', ['$scope', '$http', 'signal', function($scope, $http, signal) {
// streamers array with array of channels
var streamers = ["freecodecamp", "storbeck", "terakilobyte", "habathcx", "RobotCaleb", "thomasballinger", "noobs2ninjas", "beohoff", "medrybw"];
$scope.imgs;
$scope.signal = signal;
var self = this;
//empty array
self.info = [];
for (var i = 0; i < streamers.length; i++) {
var url = "https://api.twitch.tv/kraken/channels/";
var streamUrl = "https://api.twitch.tv/kraken/streams/";
var callback = "/?callback=JSON_CALLBACK";
//json ajax request for channels online and offline status
$http.jsonp(streamUrl + streamers[i] + callback).success(function(data) {
//provides status of shows online and offline
signal = data.stream;
console.log(signal)
});
// json ajax request for channels and channel's info
$http.jsonp(url + streamers[i] + callback).success(function(data) {
// if channel does not have a logo image, this jpg will be the placeholder
// if statement test channel status (online or offline)
if (!signal) {
signal = "offline";
} else if (signal) {
signal = 'online';
}
// pushing retreive data from twitch.tv into array self.info
self.info.push(data);
});
Your issue here is async call to $http. You need to use $http.then and chain the promises. You do not need a factory for this instance but it is best practice to have one that just returns your info object. I didn't know exactly the format you wanted so I created one. Here is the plunker: http://plnkr.co/edit/ecwk0vGMJCvkqCbZa7Cw?p=preview
var app = angular.module('Twitch', []);
// declaring my TwitchController and dependencies
app.controller('TwitchController', ['$scope', '$http', function($scope, $http) {
// streamers array with array of channels
var streamers = ['freecodecamp', 'storbeck','terakilobyte', 'habathcx', 'RobotCaleb', 'thomasballinger', 'noobs2ninjas', 'beohoff', 'medrybw' ];
//empty array
$scope.info = [];
var url = 'https://api.twitch.tv/kraken/channels/';
var streamUrl = 'https://api.twitch.tv/kraken/streams/';
var callback = '/?callback=JSON_CALLBACK';
angular.forEach(streamers, function(stream) {
//json ajax request for channels online and offline status
$http.jsonp(streamUrl + stream + callback).then(function (data) {
//provides status of shows online and offline
// json ajax request for channels and channel's info
$http.jsonp(url + stream + callback).then(function (channelData) {
// pushing retrieve data from twitch.tv into array self.info
$scope.info.push(
{
url: url + stream,
stream: stream,
status: !data.stream ? 'offline' : 'online', // ternary statement test channel status (online or offline)
logo: channelData.data.logo ? channelData.data.logo : 'placeholderlogo.jpg' // if channel does not have a logo image, this jpg will be the placeholder
}
);
});
});
});
}]);

can't seem to get progress events from node-formidable to send to the correct client over socket.io

So I'm building a multipart form uploader over ajax on node.js, and sending progress events back to the client over socket.io to show the status of their upload. Everything works just fine until I have multiple clients trying to upload at the same time. Originally what would happen is while one upload is going, when a second one starts up it begins receiving progress events from both of the forms being parsed. The original form does not get affected and it only receives progress updates for itself. I tried creating a new formidable form object and storing it in an array along with the socket's session id to try to fix this, but now the first form stops receiving events while the second form gets processed. Here is my server code:
var http = require('http'),
formidable = require('formidable'),
fs = require('fs'),
io = require('socket.io'),
mime = require('mime'),
forms = {};
var server = http.createServer(function (req, res) {
if (req.url.split("?")[0] == "/upload") {
console.log("hit upload");
if (req.method.toLowerCase() === 'post') {
socket_id = req.url.split("sid=")[1];
forms[socket_id] = new formidable.IncomingForm();
form = forms[socket_id];
form.addListener('progress', function (bytesReceived, bytesExpected) {
progress = (bytesReceived / bytesExpected * 100).toFixed(0);
socket.sockets.socket(socket_id).send(progress);
});
form.parse(req, function (err, fields, files) {
file_name = escape(files.upload.name);
fs.writeFile(file_name, files.upload, 'utf8', function (err) {
if (err) throw err;
console.log(file_name);
})
});
}
}
});
var socket = io.listen(server);
server.listen(8000);
If anyone could be any help on this I would greatly appreciate it. I've been banging my head against my desk for a few days trying to figure this one out, and would really just like to get this solved so that I can move on. Thank you so much in advance!
Can you try putting console.log(socket_id);
after form = forms[socket_id]; and
after progress = (bytesReceived / bytesExpected * 100).toFixed(0);, please?
I get the feeling that you might have to wrap that socket_id in a closure, like this:
form.addListener(
'progress',
(function(socket_id) {
return function (bytesReceived, bytesExpected) {
progress = (bytesReceived / bytesExpected * 100).toFixed(0);
socket.sockets.socket(socket_id).send(progress);
};
})(socket_id)
);
The problem is that you aren't declaring socket_id and form with var, so they're actually global.socket_id and global.form rather than local variables of your request handler. Consequently, separate requests step over each other since the callbacks are referring to the globals rather than being proper closures.
rdrey's solution works because it bypasses that problem (though only for socket_id; if you were to change the code in such a way that one of the callbacks referenced form you'd get in trouble). Normally you only need to use his technique if the variable in question is something that changes in the course of executing the outer function (e.g. if you're creating closures within a loop).

Resources