Oboe.js: reacting to Ajax/Rest quicker by not waiting for it to finish
github.com
github.com
The main difference between multipart XHR and Oboe, that I can see, is that MHXR requires you to format your data in a specific manner (using a magic delimiting character), though I'm curious if the base method is similar or not.
1 Create XHR, listen to XHR2 progress event. 2 Use Clarinet.js SAX parser, scoop up all events. 3 From SAX events, build up actual JSON and maintain path from root to actual node. 4 Match that path (+ some other stuff) against registered JSONPath specs. 5 Fire callbacks if they pass.
Yours sounds more elegant in that it can handle JSON naturally, but I wonder if the other might be better suited for binary content (not sure in which context that would make sense, if any).
Either way, I find them all fascinating, and I've starred your project :) Will keep an eye on it.
1. http://techfoolery.com/mxhr/mxhr.js
Adding XML/XPATH support would be a natural extension.
Took me about 10 minutes to add to an existing rails app that adds hundreds of markers onto a google map via json. Live updating as you scroll around.
Previously I was trying to find the sweet spot between how many slows down the initial rendering to the viewer vs showing all the markers.
Changed about 3 lines of javascript to use Oboe and changed my rails controller to:
per_page = 100
1.upto(10).each do |page|
response.stream.write ActiveModel::ArraySerializer.new( resources.paginate(page: page, per_page: per_page) , {root: 'cg'}).to_json
end
response.stream.closeHere's one: How about the fact in some cases waiting on the latency of a data lookup before rendering the page makes it feel much slower and more sluggish?
In the case mentioned above of map markers, if there's real latency involved in the lookup and you can render the page sans markers and then populate them a couple seconds later, isn't that a superior UI?
Yes, if you are building a simple, moderate or low traffic website/app, it's probably a better practice to render the page with the initial JSON needed. But a lot of the people here are working on products with millions of users -- or just tons of data -- and that changes the equation a bit.
What do you think?
While the phrasing of the final sentence was awkward, the post was arguing for exactly the same thing as you.
My question: why not modify the backend to accept a request like `/myapp/foods.json` and let the backend compose the json you need & send only that? It seems like fixing it "in the wrong place" to build your frontend to accommodate getting the wrong/too much data. Is this a contrived example that isn't the core usecase? This is my assumption.
Is this primarily for 3rd party APIs and legacy codebases where it's impractical to change the response type & updating to e.g. sockets is impractical? Thanks for the cool project, I apologize for my ignorance wrt whatever points I'm missing!
[0] https://github.com/jimhigson/oboe.js#using-objects-from-the-...
If you scroll the README file to the bottom you'll find some good use cases that explain it better than I just did :)
I got the idea for this project working on data vis. Not all of the data was visible and we wanted to display the first bit of data quicker without waiting for all of it to arrive. We could have just sent the visible bit but it was good to have some data ready in the off-screen section for when the user scrolled.
Before that I worked on a service where we were aggregating 6 or 7 services into a single JSON. Some of the services were quicker than others but because the AJAX lib we were using waited for the whole response they all had to go at the speed of the slowest component.
We could have done multiple requests but it was more elegant to serve a whole page's json in one call. Also, we cached the slow services so they were only sometimes slow.
Actually, you could use it for that, as long as the library actually finishes downloading the file on abort. If you need contents from the file again, you just download it again and as it will already be cached, that operation should be extremely fast.
For most sites there'll be some users where it will make it faster (mobile, slow internet) and others that it'll be about the same. If the network is unreliable it should help as well because when the connection drops you don't lose what you already downloaded.
"makers" = just me :-)
https://github.com/jimhigson/oboe.js#reading-from-any-stream...
oboe( fs.createReadStream( '/home/me/secretPlans.json' ) )
.node('!.schemes.*', function(scheme){
console.log('Aha! ' + scheme);
});
.node('!.plottings.*', function(deviousPlot){
console.log('Hmmm! ' + deviousPlot);
})
.done(function(){
console.log("*twiddles mustache*");
});I'm using Node.js — with an Express.js router — to power an internal site. We have a few API endpoints which would benefit from this. Does anything need changing when sending the data?
Having said that, there's a much bigger improvement if you're progressively writing out the JSON rather than doing it in one big lump.
On the server side, you may run into an error after you started writing out your reply. That may cause an incomplete reply. It may require more involved error handling between the server and client. 'Gather first write later' approach gives you a simpler error propagation between server and client.
I ask only because most of the examples on that page would not be valid as the name part of the key:value pairs needs to be quoted.
PS: Don't upvote this, it's a minor detail and the big point about the use-case is far more important than this comment.
https://github.com/dscape/clarinet
Unquoted JSON in docs is a mistake. I'll take a look now.
There's a test explicitly for not allowing this:
https://github.com/jimhigson/oboe.js/blob/master/test/specs/...
Very similar except JSON/JSONPath instead of HTML/CSS. Oboe runs fine in Node but I want to make the code a bit more standards-y. Ie, using Node's EventEmitters instead of the little pubsub I made for the browser.
Btw what are you using for the Node side, JSONStream?
The client side works in Node right now as well as in the browser but it is a bit browser-y. It is on my home office Agile board to make it a bit more node-y.
[1]: https://github.com/component/component
Do you need to stream JSON objects from a server to make this work? You have to get a response from the server in some kind of streaming protocol right?
Or...
Is this just reading the part of the JSON string that it currently has?
I'm inexperienced with streaming, so this might be obvious to some.
EDIT:
Ah, I get it. I was wrong to say it was "streaming". Looks like my second suggestion was the correct one.
Consider if you are writing data from a db: you can either collect all the rows together then write them out, or you can write them out one at a time as you get them. If you write one at a time you are in a sense streaming even if you're not using a streaming protocol.
On a slow network reading any http is like a stream because you can use the first bit to arrive without waiting for the rest. The faster the network the less the benefit but it shouldn't end up worse.
I built this for more-or-less standard downloading, only quicker. But, yeah, if you set a server up to feed it you could use it for lots of creative things.
[0]https://sites.google.com/a/webpagetest.org/docs/using-webpag...
<?
echo 'Hey<br>';
flush();
ob_flush();
sleep(20);
echo 'Bye';
?>
Will send you "Hey<br>Bye" after 20sec instead of what you would expect (which does work with gzip off).Besides, even if you manage to stream it, I imagine inflating partially received content is not trivial for the browser.
Firefox's xhr fires progress events for gzipped content but not Chrome's. Looking to see if I can find a way round it.
Here's the little test service I wrote to stream out some gzipped content:
https://github.com/jimhigson/oboe.js/blob/master/test/stream...
edit: Also how is this faster, if you're parsing an incomplete response over and over again isn't each parse blocking, wouldn't this approach kill your FPS?
Nothing gets parsed more than once. SAX parsers already parse streams, they're just not used very much because they're a pain to program with.
I might have a pet project this weekend, thanks for sharing.