Skip to content

Instantly share code, notes, and snippets.

@penguinpowernz
Last active October 25, 2021 16:17
Show Gist options
  • Save penguinpowernz/6d579ac416f2684cd422 to your computer and use it in GitHub Desktop.
Save penguinpowernz/6d579ac416f2684cd422 to your computer and use it in GitHub Desktop.
Chunked encoding parser for Javascript

Blow Chunks in your Javascript

So I couldn't find a wrapper library that made dealing with chunked encoding over javascript nice. So I made this. It helps you do long polling in javascript using chunked encoding.

Usage

This will add messages to the div with that class every time a chunk is received:

<html>
  <head>
    <meta charset="utf-8">
  </head>

  <body style="overflow: hidden;">
    <h1>Messages:</h1>

    <div class="messages"></div>

    <script src="js/subscriber.js" type="text/javascript"></script>
    <script src="js/jquery-2.1.1.js" type="text/javascript"></script>

    <script type="text/javascript">

      var s = new Chunky("http://localhost/chunked-encoding-test");
      
      s.on("chunk", function(chunk) {
        $(".messages").append($("<p>").text(chunk));
      });

    </script>
  </body>
</html>

Subscribing to redis over Webdis

This code demonstrates how you would subscribe to a redis channel using webdis :

var s = new Chunky("http://localhost:7379/SUBSCRIBE/test", {
  preprocessor: function(chunk) {
    // chunk looks like: {"SUBSCRIBE":["message","test","stuff is happening"]} 
    return JSON.parse(chunk).SUBSCRIBE[2]; // parse the webdis JSON output
  }
});

s.on("chunk", function(chunk) {
  $(".messages").append($("<p>").text(chunk)); // outputs only the message
});

It probably wouldn't be hard to wrap this with another (WebdisSubscriber) library to make subscribing easier.

function Chunky(url, config){
this.init(url, config);
}
Chunky.prototype = {
url: null,
config: null,
callbacks: null,
previous_response_length: 0,
xhr: null,
init: function(url, config) {
this.url = url;
this.config = config || {};
this.resetCallbacks();
if ( typeof(this.config.preprocessor) == "function" ) {
this.callbacks.preprocessor = this.config.preprocessor;
}
self = this;
var xhr = new XMLHttpRequest();
xhr.addEventListener("abort", this.onAborted);
xhr.addEventListener("error", this.onError);
xhr.addEventListener("load", this.onCompleted);
xhr.addEventListener("loadend", this.onClosed);
xhr.open("GET", url, true);
xhr.onreadystatechange = function() {
if (xhr.readyState == 3) {
// console.log("ready state changed")
var response = xhr.responseText;
var chunk = response.slice(self.previous_response_length);
self.previous_response_length = response.length;
// console.log("CHUNK RECEIVED");
// console.log(chunk);
chunk = self.callbacks.preprocessor(chunk);
// console.log("CHUNK PREPROCESSED")
// console.log(chunk);
// console.log(self.callbacks);
self.callbacks.chunk.forEach(function(cb) {
cb(chunk);
});
} else {
console.log("ready state not 3!!!!!")
}
}
xhr.send(null);
this.xhr = xhr;
},
on: function(event, callback) {
// console.log(event+" event has "+this.callbacks[event].length+" callbacks");
this.callbacks[event].push(callback);
},
onCompleted: function(e) {
console.log("chunky complete");
self.callbacks["complete"].forEach(function(cb) {
cb(e);
});
},
onAborted: function(e) {
console.log("chunky aborted");
self.callbacks["abort"].forEach(function(cb) {
cb(e);
});
},
onError: function(e) {
console.log("chunky errored");
self.callbacks["error"].forEach(function(cb) {
cb(e);
});
},
onClosed: function(e) {
console.log("chunky closed");
self.callbacks["close"].forEach(function(cb) {
cb(e);
});
},
resetCallbacks: function() {
this.callbacks = {
chunk: [],
complete: [],
error: [],
abort: [],
close: [],
preprocessor: function(chunk) { return chunk; }
};
},
destroy: function() {
this.resetCallbacks();
if ( this.xhr != null ) { this.xhr.abort(); }
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment