├── .gitignore ├── .travis.yml ├── README.md ├── example.html ├── example.js ├── kebab-min.js ├── kebab.js ├── package.json └── test └── kebab.js /.gitignore: -------------------------------------------------------------------------------- 1 | lib-cov 2 | *.seed 3 | *.log 4 | *.csv 5 | *.dat 6 | *.out 7 | *.pid 8 | *.gz 9 | 10 | pids 11 | logs 12 | results 13 | 14 | node_modules 15 | npm-debug.log -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - 0.6 4 | - 0.8 5 | - 0.9 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # kebab [![Build Status](https://secure.travis-ci.org/thlorenz/kebab.png)](http://travis-ci.org/thlorenz/kebab) 2 | 3 | Half queue half pubsub. Super small (< 30 loc) and simple queue that supports subscribers. 4 | 5 | ```javascript 6 | var kebab = require('kebab') 7 | , kb = kebab() 8 | , items = 10 9 | , consumers = 4; 10 | 11 | function produce () { 12 | var id = --items; 13 | 14 | if (id) { 15 | setTimeout(function () { 16 | console.log('Producer enqueueing item %d', id); 17 | kb.enqueue({ now: new Date() }, id); 18 | produce(); 19 | }, 50); 20 | } 21 | } 22 | 23 | function consume (num) { 24 | kb.once(function (info, id) { 25 | console.log( 'Consumer %s handling item %d produced at (%ds:%dms).' 26 | , num, id, info.now.getSeconds(), info.now.getMilliseconds()); 27 | 28 | // younger consumers are lazy and therefore only consume once 29 | if (num > 1) setTimeout(function () { consume(num); }, 180); 30 | }); 31 | } 32 | 33 | for (var i = 0; i < consumers; i++) consume(i); 34 | 35 | produce(); 36 | ``` 37 | 38 | Outputs: 39 | 40 | Producer enqueueing item 9 41 | Consumer 0 handling item 9 at (3s:879ms). 42 | Producer enqueueing item 8 43 | Consumer 1 handling item 8 at (3s:942ms). 44 | Producer enqueueing item 7 45 | Consumer 2 handling item 7 at (3s:995ms). 46 | Producer enqueueing item 6 47 | Consumer 3 handling item 6 at (4s:48ms). 48 | Producer enqueueing item 5 49 | Producer enqueueing item 4 50 | Consumer 2 handling item 5 at (4s:99ms). 51 | Producer enqueueing item 3 52 | Consumer 3 handling item 4 at (4s:150ms). 53 | Producer enqueueing item 2 54 | Producer enqueueing item 1 55 | Consumer 2 handling item 3 at (4s:202ms). 56 | Consumer 3 handling item 2 at (4s:252ms). 57 | Consumer 2 handling item 1 at (4s:303ms). 58 | 59 | ## Install 60 | 61 | `npm install kebab` 62 | 63 | ## Run Tests 64 | 65 | `npm test` 66 | 67 | ## Run example 68 | 69 | ### With nodejs 70 | 71 | `npm run-script example` 72 | 73 | ### In the Browser 74 | 75 | `open example.html` 76 | 77 | ## Features 78 | 79 | - works server side 80 | - works in the browser 81 | - supports async module loaders like requirejs 82 | 83 | ## API 84 | 85 | ### create a kebab 86 | 87 | ```javascript 88 | var kebab = require('kebab') 89 | , kb = kebab(); 90 | ``` 91 | 92 | ### enqueue 93 | 94 | ***kebab.enqueue(arg1 [, arg2, .., argn])*** 95 | 96 | arg1 .. argn are the arguments you want to pass when a subscriber callback is called. 97 | 98 | **Example:** 99 | 100 | ```javascript 101 | kb.enqueue(1, 'hello world'); 102 | ``` 103 | 104 | ### once 105 | 106 | ***kebab.once(callback)*** 107 | 108 | Subscribe to be called back with queued arguments. 109 | 110 | If queue is currently holding arguments, callback will be invoked with them immediately. 111 | 112 | Otherwise the callback will be invoked one time when arguments are enqueued in the future. 113 | 114 | **Example:** 115 | 116 | ```javascript 117 | kb.once(function (num, s) { console.log('working on num: %s - %s', num, s); }); 118 | ``` 119 | -------------------------------------------------------------------------------- /example.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Kebab Producer/Consumer Example 6 | 7 | 46 | 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /example.js: -------------------------------------------------------------------------------- 1 | var kebab = require('./') 2 | , kb = kebab() 3 | , items = 10 4 | , consumers = 4 5 | ; 6 | 7 | function produce () { 8 | var id = --items; 9 | 10 | if (id) { 11 | setTimeout(function () { 12 | console.log('Producer enqueueing item %d', id); 13 | kb.enqueue({ now: new Date() }, id); 14 | produce(); 15 | }, 50); 16 | } 17 | 18 | } 19 | 20 | function consume (num) { 21 | kb.once(function (info, id) { 22 | console.log( 23 | 'Consumer %s handling item %d produced at (%ds:%dms).' 24 | , num, id, info.now.getSeconds(), info.now.getMilliseconds() 25 | ); 26 | 27 | // younger consumers are lazy and therefore only consume once 28 | if (num > 1) { 29 | // heavy work going on 30 | setTimeout(function () { consume(num); }, 180); 31 | } 32 | }); 33 | } 34 | 35 | for (var i = 0; i < consumers; i++) 36 | consume(i); 37 | 38 | produce(); 39 | -------------------------------------------------------------------------------- /kebab-min.js: -------------------------------------------------------------------------------- 1 | (function(){function t(){var t=[],n=[];return{enqueue:function(){t.push(e.call(arguments));var r=n.shift();r&&r.apply(this,t.shift())},once:function(e){var r=t.shift();return r?e.apply(this,r):n.push(e)}}}var e=Array.prototype.slice;typeof define=="function"&&define.amd?define(function(){return t}):typeof window=="object"?window.kebab=t:typeof module=="object"&&typeof module.exports=="object"&&(module.exports=t)})(); -------------------------------------------------------------------------------- /kebab.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | var slice = Array.prototype.slice; 3 | 4 | function kebab() { 5 | var queue = [] 6 | , subscriptions = []; 7 | 8 | return { 9 | enqueue : function () { 10 | queue.push(slice.call(arguments)); 11 | var sub = subscriptions.shift(); 12 | if (sub) sub.apply(this, queue.shift()); 13 | } 14 | , once : function (cb) { 15 | var args = queue.shift(); 16 | return args ? cb.apply(this, args) : subscriptions.push(cb); 17 | } 18 | }; 19 | } 20 | 21 | if (typeof define === 'function' && define.amd) { 22 | define(function () { return kebab; }); 23 | } else if (typeof window === 'object') { 24 | window.kebab = kebab; 25 | } else if (typeof module === 'object' && typeof module.exports === 'object') { 26 | module.exports = kebab; 27 | } 28 | })(); 29 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "kebab", 3 | "version": "0.0.3", 4 | "description": "Half queue half pubsub. Super small (< 30 loc) and simple queue that supports subscribers.", 5 | "main": "kebab.js", 6 | "directories": { 7 | "test": "test" 8 | }, 9 | "scripts": { 10 | "test": "tap ./test/*.js", 11 | "example": "node example" 12 | }, 13 | "repository": { 14 | "type": "git", 15 | "url": "git://github.com/thlorenz/kebab.git" 16 | }, 17 | "keywords": [ 18 | "pubsub", 19 | "queue", 20 | "micro", 21 | "small", 22 | "simple" 23 | ], 24 | "author": "Thorsten Lorenz", 25 | "license": "BSD", 26 | "devDependencies": { 27 | "tap": "~0.3.1" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /test/kebab.js: -------------------------------------------------------------------------------- 1 | /*jshint asi:true*/ 2 | 3 | var kebab = require('..') 4 | , test = require('tap').test; 5 | 6 | function setup () { 7 | kb = kebab(); 8 | } 9 | 10 | test('when an item is in the queue', function (t) { 11 | setup(); 12 | t.plan(1); 13 | 14 | kb.enqueue('uno'); 15 | 16 | t.test('# once calls back immediately with that item', function (t) { 17 | t.plan(1); 18 | kb.once(function (item) { 19 | t.equal('uno', item); 20 | t.end(); 21 | }) 22 | }) 23 | }) 24 | 25 | test('when no item is in the queue', function (t) { 26 | setup(); 27 | t.plan(1); 28 | 29 | t.test('# and I listen once', function (t) { 30 | var calledBack = false; 31 | 32 | t.plan(1); 33 | 34 | kb.once(function (item) { 35 | calledBack = true; 36 | }) 37 | 38 | setTimeout(function () { 39 | t.equal(false, calledBack, 'does not immediately call back'); 40 | t.end(); 41 | }, 10); 42 | }) 43 | }) 44 | 45 | test('when no item is in the queue', function (t) { 46 | setup(); 47 | t.plan(1); 48 | 49 | t.test('# and I listen once', function (t) { 50 | var calledBack = false 51 | , calledWith; 52 | 53 | t.plan(1); 54 | 55 | kb.once(function (item) { 56 | calledBack = true; 57 | calledWith = item; 58 | }) 59 | 60 | t.test('# # and enqueue an item after', function (t) { 61 | t.plan(3); 62 | 63 | kb.enqueue('uno'); 64 | 65 | t.equal(calledBack, true, 'calls back'); 66 | t.equal('uno', calledWith, 'with queued item'); 67 | 68 | t.test('# # # and enqueue another item after', function (t) { 69 | t.plan(2); 70 | 71 | calledBack = false; 72 | kb.enqueue('dos'); 73 | 74 | t.equal(calledBack, false, 'does not call back again'); 75 | 76 | t.test('# # # # and I listen once again', function (t) { 77 | t.plan(1); 78 | 79 | kb.once(function (item) { 80 | t.equal('dos', item, 'calls back with the other item'); 81 | t.end(); 82 | }) 83 | }) 84 | }) 85 | }) 86 | }) 87 | }) 88 | 89 | test('when I queue multiple arguments', function (t) { 90 | setup(); 91 | kb.enqueue(1, 2, 'foo', { uno: 'eins' }); 92 | 93 | t.test('and I listen once', function (t) { 94 | t.plan(4); 95 | 96 | kb.once(function ($1, $2, foo, unoeins) { 97 | t.equal($1, 1, 'passes first number'); 98 | t.equal($2, 2, 'passes second number'); 99 | t.equal(foo, 'foo', 'passes string'); 100 | t.deepEqual(unoeins, { uno: 'eins' }, 'passes object'); 101 | t.end(); 102 | }) 103 | }) 104 | }) 105 | 106 | 107 | 108 | 109 | --------------------------------------------------------------------------------