{ "feed": { "id": "urn:uuid:2e8662fc-461f-58a7-9159-c22ca4880eb0", "link": [ { "@attributes": { "href": "http://darrennewton.com/atom.json", "rel": "self" } }, { "@attributes": { "href": "http://darrennewton.com/" } } ], "title" : "Miscellanea", "subtitle" : "Code, design & cultural ephemera from Darren Newton's brain", "updated" : "2016-12-21T15:18:26-05:00", "author": { "name": "Darren Newton" }, "rights" : "Copyright (c) 2016, Darren Newton", "entry": { "id" : "urn:uuid:fd998589-4fe3-5067-8353-83bd8e8b5092", "link" : "http://darrennewton.com/2013/11/16/evented-arrays-a-javascript-conveyor-belt/", "summary" : "
\n\nThis post takes a quick look at\nEventedArray, a small Array-like\nJavaScript data structure (written in CoffeeScript) that allows you to register callbacks on\naccessor/mutator operations and also create fixed size buffers. All\nexamples are in JavaScript. ", "content" : "
\n\nThis post takes a quick look at\nEventedArray, a small Array-like\nJavaScript data structure (written in CoffeeScript) that allows you to register callbacks on\naccessor/mutator operations and also create fixed size buffers. All\nexamples are in JavaScript.
\n\nThe concept of\nReactive Programming\nhas been gaining a lot of traction lately, especially when working with\nuser interfaces. To oversimplify, Reactive Programming deals with streams of data or events which can be sampled,\ncombined and observed to try an bring order to the chaos found in your\ntypical web application. A lot of very smart folks have written some\nexcellent libraries to achieve this, such as\nRxJS and\nBacon.js. Go check \u2018em out.
\n\nWhen working on small applications I often find a need for an\n\u201cobservable\u201d data structure. It would be great to have something like\nan Array which I could treat like a stack, with events triggered\nwhenever values are pushed on or shifted off. I could achieve this\nwith either of the libraries noted above, or even Backbone.js\u2019s Events\nmixin, but sometimes I just need something small and simple. Also,\nit\u2019s fun to learn how things work by rolling your own toy implementations.
\n\nI wanted this data structure to be as close to a regular\nArray\nas possible, with a standard Object Oriented type interface. The core\nlibrary\nis written in CoffeeScript but you can pull it into any JavaScript\nproject as a global or with RequireJS. Let\u2019s take a look:
\n\n:::javascript\n// Create a new data structure\nvar E = new EventedArray(1,2,3,4);\n\n// It has setters & getters\nE.set(5,6);\nE.get(4); // returns 5, the 0 indexed value of the array\nE.toString(); // \"[1,2,3,4,5,6]\"\n\n// Remove values\nE.remove(3);\nE.toString(); // \"[1,2,4,5,6]\"\n\n// Mess with the stack\nE.pop();\nE.toString(); // \"[1,2,4,5]\"\nE.shift(); // 1\nE.toString(); // \"[2,4,5]\"\n\n// Underscore collection functions\nE.each(function(i){ console.log(i*i); }); // 4 16 25\nE.map(function(i){ return i^2; }); // [0,6,7]\nE.filter(function(i){ return i%2 == 0; }); // [2,4]\n\n// Raw access to the values\nE.values; // [2,4,5]\n
\n\nSo that\u2019s cool, it behaves like an Array for the most part. Now I want\nto add some callbacks that are triggered when I set, get, shift, etc.
\n\n:::javascript\nE.register('set', function(i){ console.log(i + ' was set on E'); });\nE.set(6); // '6 was set on E'\n\nE.register('remove', function(i){ console.log(i + ' was removed!'); });\nE.remove(2); // '2 was removed!'\n
\n\nYou can register events on most of the methods available, like\nreduce
, every
, contains
and many more. Take a look at the\nsource\nto see all the methods available.
A lot of times I want to treat my data structure like a fixed size\nstack, something that will only hold n values, shifting older values\noff the front as new ones are pushed onto the end. So I went ahead and\nadded a setBuffer
method to do just that:
:::javascript\nvar E = new EventedArray();\nE.setBuffer(5);\nE.set(1,2,3,4,5,6);\nE.toString(); // \"[2,3,4,5,6]\"\nE.set(7,8,9,10);\nE.toString(); // \"[6,7,8,9,10]\"\n
\n\nSo what can we do with EventedArray? It really lends itself to\nmanaging streams of values, so here\u2019s a little DOM based animation\nexample. Go ahead and waggle your cursor around in the box:
\n\n\n\nThe code for this is pretty simple, you can view it in action\nhere, we have one\nEventedArray with a buffer of 25 storing\nBox\nobjects and a listener on the mousemove
event\npassing those Boxes in:
:::javascript\n// Create a queue to display our points\nvar displayQueue = $('#displayqueue');\nvar P = new EventedArray();\nP.setBuffer(25);\n// When points are set to this queue display them as a string\nP.register('set', function() {\n displayQueue.html(P.toString());\n});\n\n// Create a Queue that holds 25 items and attach events\nvar Q = new EventedArray();\nQ.setBuffer(25);\n\n// As Boxes are set tell them to appear and also set their\n// [x,y] to the displayQueue\nQ.register('set', function(b) {\n b.showBox();\n P.set(b.point)\n});\n\n// As boxes are shifted off, tell them to fade out\n// and null them out\nQ.register('shift', function(b) {\n b.hideBox();\n b = null;\n});\n\n// Drawing area\nvar canvas = $('#drawing');\n\n// Mousemove listener\nvar onMove = function(e) {\n Q.set(new Box([e.x, e.y], canvas)); // Make a Box\n};\n\ndocument.getElementById('drawing')\n .addEventListener('mousemove', onMove);\n
\n\nSo this nice, Boxes are pushed into the queue and shifted off, with\nevents triggering their behavior. You could wire up more elaborate\nsystems using this general concept, such as a simple\nFlickr search\nwhich filters items from one EventedArray into another:
\n\n\n\nIn this case we\u2019re using events on the filter
method from Underscore\nto shift Photos off one stack and into another:
:::javascript\n// Register a filter callback on stream which removes\n// photos from the stream itself\nstream.register('filter', function(f) {\n _.each(f, function(i) { stream.remove(i) });\n});\n\n// Filter photos from Stream based on title\n$filter_submit.on('click', function(e) {\n e.preventDefault();\n\n // make the search term lowercase\n var filter = $.trim($filter.val()).toLowerCase();\n\n // filter stream by the search term. This will trigger\n // the filter callback on stream\n var f = stream.filter(function(i) {\n return i.title.toLowerCase().indexOf(filter) !== -1;\n });\n\n // Take all the filtered photos and create new elements\n // in the filtered list\n if (f.length > 0) {\n filtered.each(function(i) { filtered.remove(i) });\n _.each(f, function(i) {\n filtered.set(new Photo(i.entry, $filtered, filtered_remove));\n });\n };\n});\n
\n\nYou could get fancy and use drag and drop events to move items from\none array to another, using events to do cleanup, trigger animations\nand such (at that point you would probably want one of the more fleshed out\nlibraries I mentioned up at the top).
\n\nEventedArray is a simple data structure. You could blow it out into\nsomething a bit more robust, registering multiple callbacks on a\nsingle event, feel free to expand it as a learning exercise.
\n\n