I looked into how it worked and found other things out.
Every page refers to /news.css. Since few HTTP headers are returned with /news.css, no caching is performed AFAICS anywhere between my browser and news.yc. So every page access makes the browser request /news.css again, doubling the number of hits.
Any unknown URL returns a HTTP 200 OK instead of, e.g. a 404, e.g. http://news.ycombinator.com/standards_what_standards . This is seen by my browser when it requests a /favicon.ico. It tries twice, presumably because it isn't keen on the HTML response. That's twice per page access to news.yc. It can't learn there's no favicon because it gets a 200 back rather than a 404.
Sorting this out would just seem nice. Pretty trivial too, I'd have thought. At the moment every dynamic page access to view a thread is generating an extra three needless HTTP requests.
On a related note, http://news.ycombinator.com/news.css returns a MIME type of text/html instead of text/css like it should. This causes warnings to appear in this browser's console.
Yeah, thanks PB. It's a big improvement and cuts down bandwidth use a lot too.
I've peered at the Javascript and most of its obvious even without knowing Javascript :-) Might someone answer a few questions?
node.href just returns an empty text/html document when fetched. Does this happen synchronously or not? What do browsers do when they're given text/html as the content of an image and what do the specs define they do? When is ping freed? Is it automatic when it goes out of scope and does that suggest that the image fetch is synchronous?
I'm trying to understand what delays occur when and whether on a larger scale you'd need to manually free some of these resources.
Nice move!
But one thing...
The server should get updated first. Only after that should the UI value change.
[Right now, its the reverse here.Is there any particular reason for such an implementation?]
It's so that if the server update fails, the user isn't mislead into thinking it succeeded. They can then take whatever action needed to correct it, like clicking the arrow again.
It probably doesn't matter much for a site like this, but it's one of those things that's just a good habit to get into. Perform validations first, then actions, then UI updates. And do the actions that are most reversible or most likely to fail first. If you have a POST that updates a DB table and sends an e-mail, update the database first, because there's no way to unsend that e-mail.
It's like comparing constants from the left in C or doing "literal".equals(var) in Java - each individual occurrence is trivial, but taken together, it can save you a lot of grief.
Edit: though looking at the source, it's done through an Image update. I don't think there's any way to trap failure with that implementation, so here, it really doesn't matter.
Yeah, it seems to work ok for reddit (try "work offline" then click the arrows on reddit).
For things that are really important (sending an email or something) I'd check the reply, but for arrow clicking it's probably not worth the effort and delay.
You could always update the UI on the assumption that it will succeed, and then check the return value. If you get failure, notify the user somehow. Making the UI seem a slight bit more responsive might not be really worth it, of course.
Definitely true, especially for something like up-voting. I mean, it might fail on 0.1% of clicks, but 0.1% accuracy on a vote is pretty good.
I guess if one was really concerned, you could update the UI immediately like News.YC does, and then change it back or show an error if it does happen to come back failed.
I think the standard solution here would be not to lie to the user. A quick sending graphic, which is replaced with a received graphic would accurately portray the events that are happening. These could easily be iconic, maybe just a differently colored arrow while sending, which fades out when it's sent.
I'm curious why you're averse to learning and using javascript, Paul. It's really close to Lisp (no compile-time macros, but functions are first class objects, has lexical closures, so on). It's really a powerful language, much more so than C.
Well, look at the code :) I did, it was educational. Node.href contains the values that are linked on the up/down arrows.
// ping server
var ping = new Image();
ping.src = node.href;
return false; // cancel browser nav
}
The clever part is in using the image object as an ajax-like connection, response isn't check, and I'm not sure if it's async. Question: is this a common javascript idiom? Is it browser portable?
Mynameishere: The answer to your question is it updates the number and makes an immediate request to the server.
Er.. So that means it's a GET request, and GET requests which modify state on a website are bad. Things like Google Web Accelerator silently visit all the links on the page and cache the result in case you decide to visit that link. So there might be some problems for people using those (For example, their accelerator might accidentally upvote all the comments in every comment page they visit)
Yep, I just confirmed this by viewing the source code, then visiting http://news.ycombinator.com/a?fnid=oO6e74w9Mq manually. It upvoted the "bandsintown" article. A misbehaved web accelerator could upvote every single article on the front page, and every comment in each comment page that they visit.
Okay, so I just downloaded Google Web Accelerator and it doesn't upvote the links.. It must be smart enough not to. I'm worried about other ones that people may be using though. (Doesn't fasterfox do some kind of prefetching?)
They can't reliably, because POST requests usually get data from forms when you click 'submit'. The other way to initiate a POST is using xmlhttprequest, setting the method to 'POST', and then sending your data.
It's just an adaptation of the old, non-js html, in which the arrows were just links, and it's written such that it will work in the old way if the js doesn't work for some reason (like I never bothered to test it with all the browsers).
Anything that assumes that GET links can be arbitrarily followed will break stuff all over the web. GWA needs all kinds of heuristics to avoid that kind of thing. (maybe they avoid sending cookies or session ids?)
> Anything that assumes that GET links can be arbitrarily followed will break stuff all over the web.
Arbitrarily performing GETs is something condoned by the HTTP 1.1 specification. Anything that assumes that GETs won't be arbitrarily followed is clueless.
You'd think people would have learned this the first time around. Don't write out-of-spec code and then complain when conforming code comes along and trashes your app. It's your bug, not theirs.
> GWA needs all kinds of heuristics to avoid that kind of thing.
How reliable. Wouldn't it be better to follow the spec so this kind of guesswork wasn't necessary?
GWA did use to follow all GETs, including those with parameters, and did cause all kinds of grief because of poorly written web sites, so they added the query parameter check to work around it.
Yes, educational as you say. node.href just returns an empty text/html document when fetched. Does this happen synchronously or not? What do browsers do when they're given text/html as the content of an image and what do the specs define they do? When is ping freed? Is it automatic when it goes out of scope and does that suggest that the image fetch is synchronous?
The sw on the server is somewhat more complex. It takes some amount of knitting closures together to e.g. allow the submission of a link via the bookmarklet to work even when it has to be interrupted by a login.
There a stupid bug - after you vote, the arrow is hidden, but it will accept clicks :-) So you can continue to upvote many times, and the score text updates. When you refresh, the actual vote is restored.
It's not a harmful bug, but it's certainly not behavior users will appreciate. I could imagine a new user not understanding the one man, one vote property of the system, then being confused later when the votes seem not to have gone through.
Adding the visual effect of idempotency on the frontend is probably a two-second fix, anyway.
44 comments
[ 15.4 ms ] story [ 263 ms ] threadEvery page refers to /news.css. Since few HTTP headers are returned with /news.css, no caching is performed AFAICS anywhere between my browser and news.yc. So every page access makes the browser request /news.css again, doubling the number of hits.
Any unknown URL returns a HTTP 200 OK instead of, e.g. a 404, e.g. http://news.ycombinator.com/standards_what_standards . This is seen by my browser when it requests a /favicon.ico. It tries twice, presumably because it isn't keen on the HTML response. That's twice per page access to news.yc. It can't learn there's no favicon because it gets a 200 back rather than a 404.
Sorting this out would just seem nice. Pretty trivial too, I'd have thought. At the moment every dynamic page access to view a thread is generating an extra three needless HTTP requests.
I've peered at the Javascript and most of its obvious even without knowing Javascript :-) Might someone answer a few questions?
node.href just returns an empty text/html document when fetched. Does this happen synchronously or not? What do browsers do when they're given text/html as the content of an image and what do the specs define they do? When is ping freed? Is it automatic when it goes out of scope and does that suggest that the image fetch is synchronous?
I'm trying to understand what delays occur when and whether on a larger scale you'd need to manually free some of these resources.
Great stuff, Pauls (and Trevor).
I think the answer boils down to this: http://paulbuchheit.blogspot.com/2007/06/wasting-time-on-things-that-really-dont.html
(This is not meant to be a nasty sounding response.)
It probably doesn't matter much for a site like this, but it's one of those things that's just a good habit to get into. Perform validations first, then actions, then UI updates. And do the actions that are most reversible or most likely to fail first. If you have a POST that updates a DB table and sends an e-mail, update the database first, because there's no way to unsend that e-mail.
It's like comparing constants from the left in C or doing "literal".equals(var) in Java - each individual occurrence is trivial, but taken together, it can save you a lot of grief.
Edit: though looking at the source, it's done through an Image update. I don't think there's any way to trap failure with that implementation, so here, it really doesn't matter.
For things that are really important (sending an email or something) I'd check the reply, but for arrow clicking it's probably not worth the effort and delay.
{update the server first} - {return a value} - {update the UI}
doesn't require much "effort".Its just a good practice. I am just suggesting Paul.
I guess if one was really concerned, you could update the UI immediately like News.YC does, and then change it back or show an error if it does happen to come back failed.
(Not criticizing, just curious)
// ping server
var ping = new Image();
ping.src = node.href;
return false; // cancel browser nav
}
The clever part is in using the image object as an ajax-like connection, response isn't check, and I'm not sure if it's async. Question: is this a common javascript idiom? Is it browser portable?
Mynameishere: The answer to your question is it updates the number and makes an immediate request to the server.
Anything that assumes that GET links can be arbitrarily followed will break stuff all over the web. GWA needs all kinds of heuristics to avoid that kind of thing. (maybe they avoid sending cookies or session ids?)
Arbitrarily performing GETs is something condoned by the HTTP 1.1 specification. Anything that assumes that GETs won't be arbitrarily followed is clueless.
You'd think people would have learned this the first time around. Don't write out-of-spec code and then complain when conforming code comes along and trashes your app. It's your bug, not theirs.
> GWA needs all kinds of heuristics to avoid that kind of thing.
How reliable. Wouldn't it be better to follow the spec so this kind of guesswork wasn't necessary?
It's just as well, too -- GETs are used so often for this kind of stuff that any web acceleration tool which ignored this would break lots of sites.
Tested with Safari 419.3.
Adding the visual effect of idempotency on the frontend is probably a two-second fix, anyway.