Posted by kyle on January 5, 2010 11:07 PM | bookmark / share: |
Perhaps it's subtle, but the draft spec for XMLHttpRequest calls for support for progressive response handling:
To rephrase for my purposes, responseText should return the intermediate contents of the response when an XMLHttpRequest is interrogated during the LOADING state. It'll take a little work to handle these partial responses as valid script, but let's first address browser support. Firefox and Webkit browsers already support this behavior if you set the Content-Type header of your response correctly. IE8 throws an exception when responseText is accessed before readyState reaches COMPLETE.
I ran a modified version of the streaming response tests I used in my last post to verify progressive XHR handling. The server returns several chunks in 100ms intervals that include script that indicates how much of the response was received before it is first handled by the browser.
For Webkit browsers, it's critical to specify a Content-Type of "text/plain" or "application/x-javascript" when returning script content to an XHR for progressive handling. Seems reasonable, but it's easy to neglect. In my testing, I didn't see any change in behavior in the presence of a "charset" param.
Note that Microsoft's documentation for XMLHttpRequest now refers the to draft specification. I'm hopeful that we'll be seeing support for progressive responses soon.
Now, since we'll be interpreting partial response content as executable script, we'll need to do something to ensure that each chunk we evaluate terminates on a complete expression. For this test, I added delimiters between valid blocks of source:
Where //--// is the delimiter. When outputting using chunked transfer encoding, you might organize code so that a delimiter is present at the end of each chunk boundary. On each readyState change, if the state is LOADING or DONE, I call a function to read the new content, identify a safe place to trim it, and append it to a buffer.
Finally, we evaluate the contents of the buffer. It's not necessary to remove the delimiter, since it's a valid JavaScript comment.
What would you use this for? Consider this technique for the response channel in your next Comet app or any time you're able to deliver part of a script response while doing expensive server side work to produce the rest.
4.7.6 The responseText attribute
The responseText attribute must return the result of running these steps:
1. If the state is not LOADING or DONE return the empty string and terminate these steps.
2. Return the text response entity body.
The responseText attribute must return the result of running these steps:
1. If the state is not LOADING or DONE return the empty string and terminate these steps.
2. Return the text response entity body.
To rephrase for my purposes, responseText should return the intermediate contents of the response when an XMLHttpRequest is interrogated during the LOADING state. It'll take a little work to handle these partial responses as valid script, but let's first address browser support. Firefox and Webkit browsers already support this behavior if you set the Content-Type header of your response correctly. IE8 throws an exception when responseText is accessed before readyState reaches COMPLETE.
I ran a modified version of the streaming response tests I used in my last post to verify progressive XHR handling. The server returns several chunks in 100ms intervals that include script that indicates how much of the response was received before it is first handled by the browser.
Bytes Buffered | |||
Configuration | Firefox 3.5 | Chrome 3.0 | IE 8 |
Tranfer-Encoding: chunked | 111 | 536 | N/A |
Content-Type: text/html Tranfer-Encoding: chunked |
111 | N/A | N/A |
Content-Type: text/plain Tranfer-Encoding: chunked |
111 | 85 | N/A |
Content-Type: application/x-javascript Tranfer-Encoding: chunked |
111 | 111 | N/A |
For Webkit browsers, it's critical to specify a Content-Type of "text/plain" or "application/x-javascript" when returning script content to an XHR for progressive handling. Seems reasonable, but it's easy to neglect. In my testing, I didn't see any change in behavior in the presence of a "charset" param.
Note that Microsoft's documentation for XMLHttpRequest now refers the to draft specification. I'm hopeful that we'll be seeing support for progressive responses soon.
Now, since we'll be interpreting partial response content as executable script, we'll need to do something to ensure that each chunk we evaluate terminates on a complete expression. For this test, I added delimiters between valid blocks of source:
window.aFunction(); // -- // window.bFunction(); // -- //
Where //--// is the delimiter. When outputting using chunked transfer encoding, you might organize code so that a delimiter is present at the end of each chunk boundary. On each readyState change, if the state is LOADING or DONE, I call a function to read the new content, identify a safe place to trim it, and append it to a buffer.
var index = 0; var buffer = ''; var DELIMITER = '//--//'; function handlePartialResponse(request) { var i = request.responseText.lastIndexOf(DELIMITER); if (i > index) { i += DELIMITER.length; var newChunk = request.responseText.substr(index, (i - index)); buffer += newChunk; index = i; flushBuffer(); } }
Finally, we evaluate the contents of the buffer. It's not necessary to remove the delimiter, since it's a valid JavaScript comment.
function flushBuffer() { window.eval(buffer); buffer = ''; }
What would you use this for? Consider this technique for the response channel in your next Comet app or any time you're able to deliver part of a script response while doing expensive server side work to produce the rest.
Comments
Neat idea. Instead of:
request.responseText.lastIndexOf(DELIMITER);
you might want to use:
request.responseText.indexOf(DELIMITER, index);
to avoid losing packets if more than one comes in during an event for some reason.
Posted by: Matt Mastracci | January 6, 2010 12:46 PM
I use this technique for the comet client side in all browsers but IE. Works great.
Why not just use newline for your delimiter? Yes it forces developers to keep their updates to a single line, but it's simpler and doesn't rely on a specially formatted comment.
Matt makes a good point that multiple updates can come in during a polling interval. By using newline, you can just split the response on \n, and then parse each entry in the array. Seems faster and cleaner than parsing as string.
Also, application/octet-stream will also progressively download in webkit browsers.
Posted by: ben | January 12, 2010 12:28 PM
I've gotten IE to stream to the client, but you have to use it's ActiveX foundation and open a series of nested iFrames to establish persistent communications. The up shot is that it's significantly more reliable than streaming XHR And the buffer can be flushed without resetting the connection.
Posted by: Richard Corsale | February 3, 2010 7:12 PM
This site is great!!
Posted by: Luigi Fulk | March 19, 2010 4:34 PM