Added promise support

This commit is contained in:
Kirill Efimov
2016-01-22 20:32:10 +03:00
committed by Alex Kocharin
parent f333e58640
commit 9cf8df8c22
8 changed files with 417 additions and 6 deletions

4
.lint
View File

@@ -24,7 +24,9 @@ predef+ setTimeout, clearTimeout
./test/index.js
./test/ext/max-age.js
predef+ setTimeout
predef+ setTimeout, Promise, global
./test/ext/promise.js
predef+ Promise, setTimeout, global
./benchmark
sub

View File

@@ -11,7 +11,7 @@ var callable = require('es5-ext/object/valid-callable')
extensions.dispose = function (dispose, conf, options) {
var del;
callable(dispose);
if (options.async && extensions.async) {
if ((options.async && extensions.async) || (options.promise && extensions.promise)) {
conf.on('deleteasync', del = function (id, result) {
apply.call(dispose, null, slice.call(result.args, 1));
});

50
ext/promise.js Normal file
View File

@@ -0,0 +1,50 @@
// Support for functions returning promise
'use strict';
var nextTick = require('next-tick')
, create = Object.create;
require('../lib/registered-extensions').promise = function (tbi, conf) {
var cache = create(null);
// After not from cache call
conf.on('set', function (id, __, promise) {
promise.then(function () {
// nextTick avoids error interception
nextTick(function () {
cache[id] = promise;
conf.emit('setasync', id, 1);
});
}, function () {
nextTick(function () {
conf.delete(id);
});
});
});
// From cache (sync)
conf.on('get', function (id, args, context) {
conf.emit('getasync', id, args, context);
});
// On delete
conf.on('delete', function (id) {
var result;
// If false, we don't have value yet, so we assume that intention is not
// to memoize this call. After value is obtained we don't cache it but
// gracefully pass to callback
if (!cache[id]) return;
result = cache[id];
delete cache[id];
conf.emit('deleteasync', id, result);
});
// On clear
conf.on('clear', function () {
var oldCache = cache;
cache = create(null);
conf.emit('clearasync', oldCache);
});
};

View File

@@ -26,6 +26,7 @@ module.exports = function (fn/*, options*/) {
// Assure extensions
if (options.async) require('./ext/async');
if (options.promise) require('./ext/promise');
if (options.dispose) require('./ext/dispose');
if (options.maxAge) require('./ext/max-age');
if (options.max) require('./ext/max');

View File

@@ -49,7 +49,7 @@ module.exports = function (original, length, options) {
throw customError("Circular invocation", 'CIRCULAR_INVOCATION');
}
cache[id] = result;
if (setListeners) conf.emit('set', id);
if (setListeners) conf.emit('set', id, null, result);
return result;
}, memLength);
} else if (length === 0) {
@@ -65,7 +65,7 @@ module.exports = function (original, length, options) {
throw customError("Circular invocation", 'CIRCULAR_INVOCATION');
}
cache.data = result;
if (setListeners) conf.emit('set', 'data');
if (setListeners) conf.emit('set', 'data', null, result);
return result;
};
} else {
@@ -83,7 +83,7 @@ module.exports = function (original, length, options) {
throw customError("Circular invocation", 'CIRCULAR_INVOCATION');
}
cache[id] = result;
if (setListeners) conf.emit('set', id);
if (setListeners) conf.emit('set', id, null, result);
return result;
};
}

View File

@@ -34,6 +34,7 @@
"timers-ext": "0.1"
},
"devDependencies": {
"plain-promise": "^0.1.1",
"tad": "~0.2.3",
"xlint": "~0.2.2",
"xlint-jslint-medikoo": "~0.1.4"

View File

@@ -1,7 +1,8 @@
'use strict';
var memoize = require('../..')
, nextTick = require('next-tick');
, nextTick = require('next-tick')
, Promise = global.Promise || require('plain-promise');
require('../../ext/async');
@@ -52,6 +53,60 @@ module.exports = function () {
}, 90);
}, 20);
},
Promise: function (a, d) {
var mfn, fn, i = 0;
fn = function (x, y) {
return new Promise(function (res) {
++i;
res(x + y);
});
};
mfn = memoize(fn, { promise: true, maxAge: 100 });
mfn(3, 7).then(function (res) {
a(res, 10, "Result #1");
});
mfn(5, 8).then(function (res) {
a(res, 13, "Result #2");
});
mfn(3, 7).then(function (res) {
a(res, 10, "Result #3");
});
mfn(3, 7).then(function (res) {
a(res, 10, "Result #4");
});
mfn(5, 8).then(function (res) {
a(res, 13, "Result #5");
});
setTimeout(function () {
a(i, 2, "Called #2");
mfn(3, 7).then(function (res) {
a(res, 10, "Again: Result #1");
});
mfn(5, 8).then(function (res) {
a(res, 13, "Again: Result #2");
});
setTimeout(function () {
a(i, 2, "Again Called #2");
mfn(3, 7).then(function (res) {
a(res, 10, "Again: Result #1");
});
mfn(5, 8).then(function (res) {
a(res, 13, "Again: Result #2");
});
nextTick(function () {
a(i, 4, "Call After clear");
d();
});
}, 100);
}, 20);
},
Async: function (a, d) {
var mfn, fn, u = {}, i = 0;
fn = function (x, y, cb) {

302
test/ext/promise.js Normal file
View File

@@ -0,0 +1,302 @@
'use strict';
var memoize = require('../..')
, Promise = global.Promise || require('plain-promise')
, nextTick = require('next-tick');
module.exports = function () {
return {
Regular: {
Success: function (a, d) {
var mfn, fn, i = 0, invoked = 0;
fn = function (x, y) {
return new Promise(function (res) {
++i;
res(x + y);
});
};
mfn = memoize(fn, { promise: true });
mfn(3, 7).then(function (res) {
++invoked;
a(res, 10, "Result #1");
}, a.never);
mfn(3, 7).then(function (res) {
++invoked;
a(res, 10, "Result #2");
}, a.never);
mfn(5, 8).then(function (res) {
++invoked;
a(res, 13, "Result B #1");
}, a.never);
mfn(3, 7).then(function (res) {
++invoked;
a(res, 10, "Result #3");
}, a.never);
mfn(5, 8).then(function (res) {
++invoked;
a(res, 13, "Result B #2");
}, a.never);
setTimeout(function () {
a(i, 2, "Init Called");
a(invoked, 5, "Cb Called");
mfn(3, 7).then(function (res) {
++invoked;
a(res, 10, "Again: Result");
}, a.never);
mfn(5, 8).then(function (res) {
++invoked;
a(res, 13, "Again B: Result");
}, a.never);
setTimeout(function () {
a(i, 2, "Init Called #2");
a(invoked, 7, "Cb Called #2");
mfn.delete(3, 7);
mfn(3, 7).then(function (res) {
++invoked;
a(res, 10, "Again: Result");
}, a.never);
mfn(5, 8).then(function (res) {
++invoked;
a(res, 13, "Again B: Result");
}, a.never);
setTimeout(function () {
a(i, 3, "Init After delete");
a(invoked, 9, "Cb After delete");
d();
}, 10);
}, 10);
}, 10);
},
Error: function (a, d) {
var mfn, fn, i = 0, e = new Error("Test");
fn = function (x, y) {
return new Promise(function (res, rej) {
++i;
rej(e);
});
};
mfn = memoize(fn, { promise: true, dispose: a.never });
mfn(3, 7).then(a.never, function (err) {
a(err, e, "Result #1");
});
mfn(3, 7).then(a.never, function (err) {
a(err, e, "Result #2");
});
mfn(5, 8).then(a.never, function (err) {
a(err, e, "Result B #1");
});
mfn(3, 7).then(a.never, function (err) {
a(err, e, "Result #3");
});
mfn(5, 8).then(a.never, function (err) {
a(err, e, "Result B #2");
});
setTimeout(function () {
a(i, 2, 'Called #2');
mfn(3, 7).then(a.never, function (err) {
a(err, e, "Again: Result");
});
mfn(5, 8).then(a.never, function (err) {
a(err, e, "Again B: Result");
});
setTimeout(function (err) {
a(i, 4, "Again Called #2");
d();
}, 10);
}, 10);
}
},
Primitive: {
Success: function (a, d) {
var mfn, fn, i = 0;
fn = function (x, y) {
return new Promise(function (res) {
++i;
res(x + y);
});
};
mfn = memoize(fn, { promise: true, primitive: true });
mfn(3, 7).then(function (res) {
a(res, 10, "Result #1");
}, a.never);
mfn(3, 7).then(function (res) {
a(res, 10, "Result #2");
}, a.never);
mfn(5, 8).then(function (res) {
a(res, 13, "Result B #1");
}, a.never);
mfn(3, 7).then(function (res) {
a(res, 10, "Result #3");
}, a.never);
mfn(5, 8).then(function (res) {
a(res, 13, "Result B #2");
}, a.never);
setTimeout(function () {
a(i, 2, "Called #2");
mfn(3, 7).then(function (res) {
a(res, 10, "Again: Result");
}, a.never);
mfn(5, 8).then(function (res) {
a(res, 13, "Again B: Result");
}, a.never);
setTimeout(function () {
a(i, 2, "Again Called #2");
mfn.delete(3, 7);
mfn(3, 7).then(function (res) {
a(res, 10, "Again: Result");
}, a.never);
mfn(5, 8).then(function (res) {
a(res, 13, "Again B: Result");
}, a.never);
setTimeout(function () {
a(i, 3, "Call After delete");
d();
}, 10);
}, 10);
}, 10);
},
Error: function (a, d) {
var mfn, fn, i = 0, e = new Error("Test");
fn = function (x, y) {
return new Promise(function (res, rej) {
++i;
rej(e);
});
};
mfn = memoize(fn, { promise: true, primitive: true });
mfn(3, 7).then(a.never, function (err) {
a(err, e, "Result #1");
});
mfn(3, 7).then(a.never, function (err) {
a(err, e, "Result #2");
});
mfn(5, 8).then(a.never, function (err) {
a(err, e, "Result B #1");
});
mfn(3, 7).then(a.never, function (err) {
a(err, e, "Result #3");
});
mfn(5, 8).then(a.never, function (err) {
a(err, e, "Result B #2");
});
setTimeout(function () {
a(i, 2, 'Called #2');
mfn(3, 7).then(a.never, function (err) {
a(err, e, "Again: Result");
});
mfn(5, 8).then(a.never, function (err) {
a(err, e, "Again B: Result");
});
setTimeout(function (err) {
a(i, 4, "Again Called #2");
d();
}, 10);
}, 10);
},
"Primitive null arg case": function (a, d) {
var mfn, x = {};
mfn = memoize(function f(id) {
return new Promise(function (res) { res(x); });
}, {
promise: true,
primitive: true
});
mfn(null).then(function (res) {
a.deep(res, x, "Args");
d();
}, a.never);
}
},
"Sync Clear": function (a, d) {
var mfn, fn;
fn = function (x) {
return new Promise(function (res) {
nextTick(function () {
res(x);
});
});
};
mfn = memoize(fn, { promise: true });
mfn(1).then(function (res) {
a(res, 1, "First");
}, a.never);
mfn(2).then(function (res) {
a(res, 2, "Second");
d();
}, a.never);
},
"Sync Clear: Primitive": function (a, d) {
var mfn, fn;
fn = function (x) {
return new Promise(function (res) {
nextTick(function () {
res(x);
});
});
};
mfn = memoize(fn, { promise: true, primitive: true });
mfn(1).then(function (res) {
a(res, 1, "First");
}, a.never);
mfn(2).then(function (res) {
a(res, 2, "Second");
d();
}, a.never);
}
};
};