I'm trying to implement a version of this intro to RxJS (fiddle here) that instead of picking a random object from a returned API array, it consumes a backthrottled stream of objects from the returned API array.
Here's a portion of the code that produces a controlled Observable from the API response (full fiddle here):
var responseStream = requestStream.flatMap(function (requestUrl) {
return Rx.Observable.fromPromise(fetch(requestUrl));
}).flatMap(function(response) {
return Rx.Observable.fromPromise(response.json());
}).flatMap(function(json) {
return Rx.Observable.from(json);
}).controlled();
I just dump each emitted user in console.log, and use a click event stream to trigger the request() call in the controlled Observable:
responseStream.subscribe(function(user) {
console.log(user);
});
refreshClickStream.subscribe(function (res) {
responseStream.request(1);
});
There's about 50 user objects returned from the GitHub API, and I'd like to backthrottle-consume them one per click (as seen above). However, after I'm fresh out of user objects I'd like to send in another call to requestStream to fetch another API call, replenish the responseStream and continue providing user objects to console.log upon each click. What would be the RxJS-friendly way to do so?
I'd do it similarly to the article example with combineLatest() although I wonder if there's an easier way than mine.
I'm making request for only 3 items. Working with 3 items is hardcoded so you'll want to modify this. I was thinking about making it universal but that would require using Subject and made it much more complicated so I stayed with this simple example.
Also, I'm using concatMap() to trigger fetching more data. However, just clicking the link triggers just the combineLatest() which emits another item from the array.
See live demo: https://jsfiddle.net/h3bwwjaz/12/
var refreshButton = document.querySelector('#main');
var refreshClickStream = Rx.Observable.fromEvent(refreshButton, 'click')
.startWith(0)
.scan(function(acc, val, index) {
return index;
});
var usersStream = refreshClickStream
.filter(function(index) {
return index % 3 === 0;
})
.concatMap(function() {
var randomOffset = Math.floor(Math.random() * 500);
var url = 'https://api.github.com/users?since=' + randomOffset + '&per_page=3';
return Rx.Observable.fromPromise(fetch(url))
.flatMap(function(response) {
return Rx.Observable.fromPromise(response.json());
});
})
.combineLatest(refreshClickStream, function(responseArray, index) {
return responseArray[index % 3];
})
.distinct();
usersStream.subscribe(function(user) {
console.log(user);
});
I use refreshClickStream twice:
to emit next item in the array in combineLatest()
to check whether this is the end of the array and we need to make another request (that's the filter() operator).
At the end distinct() is required because when you click index % 3 === 0 time triggers in fact two emission. First is the one from downloading the data and the second one is directly in combineLatest() that we want to ignore because we don't want to iterate the same data again. Thanks to distinct() it's ignored and only the new values is passed.
I was trying to figure out a method without using distinct() but I couldn't find any.
Related
Folks I'm using React Native Firebase and I want to achieve as you can see in the attached ss
there's message key and it has threads underneath and each thread have multiple messages.I want to do like I want get last item from every thread and put it into an array.How to achieve.I'm trying to use foreach and not getting my desired result
here is my code
messagesRef.on('value', snapshot => {
let messsagesFB = [];
snapshot.forEach(element => {
messsagesFB = [...messsagesFB, element.val()];
});
});
I know my code will push every element but i want to push only last item from each thread
For your database reference messagesRef, try adding .orderByKey().limitToLast(1) for the query, in which case a loop would not be needed.
messagesRef.orderByKey().limitToLast(1).on('value', snapshot => {
// TODO: Add to array
}
There's also always the catch for the last iteration of a jQuery foreach loop. In your case, you could simply do:
snapshot.forEach(function(element, idx, messagesFB) {
if (idx === messagesFB.length - 1){
messsagesFB.push(element.val());
}
});
I am a beginner and using $.get to retrieve data from a rest API such as:
[{"id":"1","url":"http:\/\/123.456.78.910\/workforce\/images\/item1.jpg","price":"99","description":"Mobile Phone"},
{"id":"2","url":"http:\/\/123.456.78.910\/workforce\/images\/item2.jpg","price":"98","description":"Laptop"}
{"id":"3","url":"http:\/\/123.456.78.910\/workforce\/images\/item3.jpg","price":"92","description":"Console"}] }
$.get('http://xxxxxxxxxxx,
function (data) {
var obj = $.parseJSON(data);
So from what I understand I have retrieved the data from the REST API and parsed it so it is stored in a variable called obj.
My question is, how do I access and use each unique record in the obj variable?
Each record has it's own picture (item1.jpg, item2.jpg etc).
Whem my app loads I want it to show the item1.jpg image, and I want to be able to navigate to the other item pictures using buttons (previous / next).
I also want the description and price to be displayed underneath in some text input fields.
What I have figured so far is that I should:
Iterate through the obj variable, and store each record into an array.
Upon app initialisation I can set the default value for the image placeholder to array[index0].url, and set the description and price fields.
I can then set the previous and next buttons to array[currentIndex-1] or array[currentIndex+1].
Would this be the best way to do it?
Or can I just do this without using an array and manipulate the obj.data directly?
Thanks!!!
I may not be understanding what exactly what you want to do but I think I have the gist. If you just want to show the picture then the array of just images probably wouldn't be a bad idea. However, it looks like the Jason you're getting is already in an array. You can just use array index notation to get to what you want.
ie)
var arr = //your json response ;
var current = 0; //sets currently displayed object to the first in the array
var setCurrent = function () {
var image = arr[current]["url"];
}
You can then modify current however you want (on click on arrow iterate up/down, etc) then call the setCurrent function to set your image the the one you want. Hope that helps!
You can use the response you have from $.get() directly.
It is an array of objects.
You can use it like this:
console.log(data[2].description);
// outputs: "Console"
I've made a CodePen demo where it has a 4th object with a real image url to show you how to use the url info...
EDIT
Just in case you wouldn't know this:
You can use the response inside the scope of the $.get() callback...
You can not use it straith after the $.get() outside the callback since $.get() is asynchronous.
You can use it in some other handler wich will happen after the response is received.
var getResponse;
$.get('http://xxxxxxxxxxx', function (data) {
getResponse = data;
console.log(data[2].description);
// outputs: "Console"
console.log(getResponse[2].description);
// outputs: "Console"
});
console.log(getResponse[2].description);
// outputs: "Undefined"
// But since this handler will be triggered long after the response is obtained:
$("#somebutton").click(function(){
console.log(getResponse[2].description);
// outputs: "console"
});
In order for your page javascript to be able to access the data retrieved from your ajax request, you'll need to assign it to some variable which exists outside the callback function.
You will need to wait until the ajax request has been processed before you can read the array. So you might want to set the actual default image to be something that doesn't rely on the ajax request (a local image).
Here's a simple approach
// fake testing ajax func
function fakeget (url, callback) {
setTimeout(callback(JSON.stringify([
{"id":"1","url":"http:\/\/123.456.78.910\/workforce\/images\/item1.jpg","price":"99","description":"Mobile Phone"}, {"id":"2","url":"http:\/\/123.456.78.910\/workforce\/images\/item2.jpg","price":"98","description":"Laptop"},
{"id":"3","url":"http:\/\/123.456.78.910\/workforce\/images\/item3.jpg","price":"92","description":"Console"}
])), 1000);
}
// real code starts here
// global variables for ajax callback and setImg func to update
var imageData, currentImg;
// change this back to $.get for real
fakeget('http://xxxxxxxxxxx',
function (data) {
imageData = $.parseJSON(data);
setImg(0);
}
);
function setImg(index) {
// turns negative indices into expected "wraparound" index
currentImg = (index % imageData.length + imageData.length) % imageData.length;
var r = imageData[currentImg];
$("#theImg").attr('src', r.url);
$('#theDescription').text(r.price + " " + r.description);
}
$("#prev").click(function () {
setImg(currentImg - 1);
});
$("#next").click(function () {
setImg(currentImg + 1);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div>
<img id='theImg' src='somedefault.jpg'>
<div id='theDescription'></div>
</div>
<button id='prev'>Prev</button>
<button id='next'>Next</button>
Few observations :
Your JSON Object is not a valid JSON.
No need to parse it again your data is already a JSON Object.
Working fiddle
var data = [{"id":"1","url":"http:\/\/123.456.78.910\/workforce\/images\/item1.jpg","price":"99","description":"Mobile Phone"},{"id":"2","url":"http:\/\/123.456.78.910\/workforce\/images\/item2.jpg","price":"98","description":"Laptop"}, {"id":"3","url":"http:\/\/123.456.78.910\/workforce\/images\/item3.jpg","price":"92","description":"Console"}];
for (var i in data) {
var imgUrl = data[i].url;
console.log(imgUrl);
}
I am using algolia javascript api for retrieving all records in my index using browse function, but still it is returning 1000 records. Here is my code:
function load_location_list(){
var client = algoliasearch('ID', 'KEY');
var index_name = "locations_new";
var attribute_list = "*";
var index = client.initIndex(index_name);
index.browse({
"attributesToRetrieve": attribute_list,
}).then(function search_Success(response) {
console.log(response);
});
}
Actually, browse doesn't return more than 1000 elements at the first call. However, the response contains a cursor that you can use to access the next elements with the browseFrom function.
However, the previous method is kind of manual. You probably want to use the browseAll function instead which lets you access all the elements sequentially.
You can find more informations about all the browse* functions in the README of the JS client (also available in the Algolia documentation).
I have an observable with a subscriber that that updates the ui.
someObservable.forEach(function(item) {
updateUI(item);
});
My issue is that after some specific user actions I want to ignore
N items from this stream, and handle it slightly
differently (instead of the default handler).
Say the user does an action which I know will produce four items
in the observable, so I want to postpone updating until the
last of these items has arrived.
I was looking into merging this stream with a "control" stream,
and use groupJoin or something to truncate it, but as far as I
can see this will only work with a time window, and not a count
of items, or even a specific order.
I'd have a variable customActivity somewhere and write the forEach's action roughly like this (my JavaScript knowledge is limited):
customActivity = function(item) {
received++;
handle(item);
if (received == 4) {
customActivity = null;
return true;
}
return false;
}
someObservable.forEach(function(item) {
if (customActivity == null || customActivity(item)) {
updateUI(item);
}
});
You can use take operator to handle only first N values and skip to N of them.
someObservable
.take(4)
.forEach(function(item) {
updateUI(item);
});
someObservable
.skip(4)
.forEach(function(item) {
// ... some code
});
Say the user does an action which I know will produce four items in the observable, so I want to postpone updating until the last of these items has arrived.
In this case bufferWithCount can do the job.
someObservable
.bufferWithCount(4)
.forEach(function(items) {
// will be called for batch of every 4 items
});
Here is a slimmed down version of my code (without the initialization calls). Everything works perfectly, except that every consecutive call I make passing in an until value into FB.api, returns me limit/2. I have tried waiting as much as 1 minute in between the calls, using different Facebook accounts but it does not help the matter.
I checked the next parameter in the returned object, and the URL simply adds the until parameter with the exact same number as my dataUltimoPost. When I call the URL, it too returns half the posts.
I found other posts with inconsistent returns here and here, but none address the paging aspect directly. Coincidentally, I came across this post which uses the same logic as my code to page posts. So I think I'm on the right track.
functions:
function getPost (success, append) {
var params = { limit: 25, date_format: 'U' };
if (append)
params.until = dataUltimoPost;
FB.api('/me/home', 'get', params, function (userData) {
if (userData.data.length > 0)
dataUltimoPost = userData.data[userData.data.length - 1].created_time - 1;
success.call(this, userData.data, append);
});
};
function fillPost(posts, append) {
var postsHTML = '';
append = append === true;
alert('posts.length: ' + posts.length + ' - append: ' + append);
}
calling the functions:
getPost(fillPost); //returns 25
getPost(fillPost, true); //returns 12
getPost(fillPost, true); //returns 1 or 0
Here's a blog post from Facebook themselves for the reason why you don't get back 25 results when asking for 25.
http://developers.facebook.com/blog/post/478/
Here they've clearly admitted that their FQL filtering mechanism pre-limits the result set BEFORE applying filtering. Here's a visual on what's happening.