Skip to content

Commit cc9c430

Browse files
committed
refactor: move to es6, Maps and Sets
BREAKING CHANGE: moves to es6, works with node 4+
1 parent 9c7c542 commit cc9c430

12 files changed

+236
-294
lines changed

.babelrc

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"plugins": [
3+
"transform-es2015-spread",
4+
"transform-es2015-destructuring",
5+
"transform-strict-mode",
6+
"transform-es2015-parameters",
7+
"transform-es2015-shorthand-properties",
8+
"transform-object-rest-spread",
9+
"transform-class-properties"
10+
]
11+
}

.editorconfig

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ root = true
33

44
[*]
55
indent_style = space
6-
indent_size = 4
6+
indent_size = 2
77
end_of_line = lf
88
charset = utf-8
99
trim_trailing_whitespace = true

.eslintrc

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"extends": "airbnb/base",
3+
"parser": "babel-eslint",
4+
"rules": {
5+
"no-param-reassign": [2,{"props": false}],
6+
"max-len": [2, 150],
7+
}
8+
}

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,4 @@ tags
3030
.cache
3131

3232
reports
33+
lib

.jshintrc

-22
This file was deleted.

.npmignore

+1
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,4 @@ dist
2525
TAGS
2626
tags
2727
.tags
28+
src

LICENSE

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
The MIT License (MIT)
22

3-
Copyright (c) 2015 Vitaly Aminev
3+
Copyright (c) 2015-2016 Vitaly Aminev
44

55
Permission is hereby granted, free of charge, to any person obtaining a copy
66
of this software and associated documentation files (the "Software"), to deal

README.md

+17-22
Original file line numberDiff line numberDiff line change
@@ -15,36 +15,31 @@ One example would be conserving resources spent on outgoing http requests.
1515
2. `callbackQueue.remove(String, Error)`: calls queued callbacks with Error argument
1616

1717
```js
18-
var callbackQueue = require('callback-queue');
19-
var request = require('request');
18+
const callbackQueue = require('callback-queue');
19+
const request = require('request');
2020

2121
function performOutgoingRequest(url, next) {
22-
var callback = callbackQueue.add(url, next);
23-
if (!callback) {
24-
return;
25-
}
22+
const callback = callbackQueue.add(url, next);
23+
if (!callback) {
24+
return;
25+
}
2626

27-
request(url, callback);
27+
request(url, callback);
2828
}
2929

30-
for (var i = 0; i < 100; i++) {
31-
32-
// request itself will be performed just once
33-
performOutgoingRequest('https://google.com', function niceCallback(err, resp, body) {
34-
// will be called 100 times
35-
});
36-
30+
for (let i = 0; i < 100; i++) {
31+
// request itself will be performed just once
32+
performOutgoingRequest('https://google.com', function niceCallback(err, resp, body) {
33+
// will be called 100 times
34+
});
3735
}
3836

39-
for (var x = 0; x < 100; x++) {
40-
41-
// request itself will be performed just once
42-
performOutgoingRequest('https://google.com', function niceCallback(err, resp, body) {
43-
// will be called 100 times with Error object
44-
});
45-
37+
for (let x = 0; x < 100; x++) {
38+
// request itself will be performed just once
39+
performOutgoingRequest('https://google.com', function niceCallback(err, resp, body) {
40+
// will be called 100 times with Error object
41+
});
4642
}
4743

4844
callbackQueue.remove('https://google.com', new Error('cancel it!'));
49-
5045
```

index.js

-134
This file was deleted.

package.json

+16-8
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
{
22
"name": "callback-queue",
3-
"version": "1.1.1",
3+
"version": "2.0.0",
44
"description": "Put your callbacks into queue to make sure that concurrent requests that you might want to perform will only be executed once",
5-
"main": "index.js",
5+
"main": "./lib/callback-queue.js",
66
"scripts": {
7-
"test": "multi='spec=- xunit=test-report.xml' ./node_modules/.bin/mocha -R mocha-multi ./test/index.js"
7+
"test": "./node_modules/.bin/mocha -r babel-register -R spec test",
8+
"prepublish": "npm run compile",
9+
"pretest": "npm run compile",
10+
"compile": "./node_modules/.bin/babel -d ./lib src"
811
},
912
"repository": {
1013
"type": "git",
@@ -22,12 +25,17 @@
2225
"author": "Vitaly Aminev <[email protected]>",
2326
"license": "MIT",
2427
"dependencies": {
25-
"debug": "^2.1.3",
26-
"lodash.omit": "^3.1.0"
28+
"debug": "^2.2.0"
2729
},
2830
"devDependencies": {
29-
"chai": "^2.2.0",
30-
"mocha": "^2.2.1",
31-
"mocha-multi": "^0.6.0"
31+
"babel-cli": "^6.4.5",
32+
"babel-eslint": "^5.0.0",
33+
"babel-preset-es2015": "^6.3.13",
34+
"babel-preset-stage-0": "^6.3.13",
35+
"babel-register": "^6.5.2",
36+
"chai": "^3.5.0",
37+
"eslint": "^1.10.3",
38+
"eslint-config-airbnb": "^5.0.1",
39+
"mocha": "^2.4.5"
3240
}
3341
}

src/callback-queue.js

+85
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
const debug = require('debug')('callback-queue');
2+
const assert = require('assert');
3+
4+
/**
5+
* Callback queue
6+
* @type {Map}
7+
*/
8+
const callbackQueue = new Map();
9+
// cache reference
10+
const nextTick = typeof setImmediate === 'function' && setImmediate || process.nextTick;
11+
12+
/**
13+
* Iterates over callbacks and calls them with passed args
14+
* @param {Array} bucket
15+
* @param {Array} args
16+
*/
17+
function iterateOverCallbacks(bucket, args) {
18+
// set iterator
19+
for (const thunk of bucket) {
20+
nextTick(thunk, ...args);
21+
}
22+
}
23+
24+
/**
25+
* Adds callback into queue based on `key` argument
26+
* If this is the first callback in the queue, wrapped callback will
27+
* be returned, which should be called when any function that you want to perform
28+
* will complete. If there are already callbacks in the queue, returns Boolean false,
29+
* which indicates that you must not call the function, as it was already called and is
30+
* currently in progress
31+
*
32+
* @param {String} key - unique key, based on which requests are bucketed
33+
* @param {Function} callback - callback that should be added into requests queue
34+
*/
35+
exports.add = function add(key, callback) {
36+
assert.equal(typeof key, 'string', 'key must be a truthy string');
37+
assert.ok(key, 'key must be a truthy string');
38+
assert.equal(typeof callback, 'function', 'callback must be a function');
39+
40+
const bucket = callbackQueue.get(key);
41+
if (bucket) {
42+
bucket.add(callback);
43+
return false;
44+
}
45+
46+
// push new set into queue
47+
callbackQueue.set(key, new Set([callback]));
48+
49+
return function queuedCallback(...args) {
50+
// its essential that we do not use any reference, because of GC
51+
// when object reaches certain number of nullified values - its recreated using compactObject
52+
// function. Therefore we need to grab a reference when callback needs to be invoked and not at
53+
// other time
54+
const callbacks = callbackQueue.get(key);
55+
if (!callbacks) {
56+
debug('Callbacks couldn\'t be invoked');
57+
return null;
58+
}
59+
60+
debug('calling callback for key %s', key);
61+
callbackQueue.delete(key);
62+
return iterateOverCallbacks(callbacks, args);
63+
};
64+
};
65+
66+
/**
67+
* Call this if you are absolutely sure you need to abort the request
68+
* Every callback in the queue will be called with a passed error object.
69+
* Make sure that previously returned callback is not called at a later time,
70+
* because if you create a bucket after removing it and then previously returned
71+
* callback is executed, it will introduces bugs into your code
72+
*
73+
* @param {String} key
74+
* @param {Error} error
75+
*/
76+
exports.remove = function remove(key, error) {
77+
const bucket = callbackQueue.get(key);
78+
if (!bucket) {
79+
return false;
80+
}
81+
82+
assert.ok(error instanceof Error, 'you must pass an instance of Error object when canceling requests');
83+
callbackQueue.delete(key);
84+
return iterateOverCallbacks(bucket, [error]);
85+
};

0 commit comments

Comments
 (0)