metarhia / metasync Goto Github PK
View Code? Open in Web Editor NEWAsynchronous Programming Library for JavaScript & Node.js
Home Page: https://metarhia.com
License: MIT License
Asynchronous Programming Library for JavaScript & Node.js
Home Page: https://metarhia.com
License: MIT License
@tshemsedinov, what do you think about turning MetaSync (under some new name) into a full-blown generic library of utility functions for JavaScript with asynchronous utilities being only a part of it? We definitely need some sort of common library since I've encountered situations when two analogous functions have to be fixed twice in Impress and JSTP.
Refs: metarhia/Metarhia#2
// expect 4 data keys
var dc1 = new metasync.DataCollector(4);
// expect 4 data keys within 5 second timeout
var dc2 = new metasync.DataCollector(4, 5000);
dc1.on('error', function(err, key) { ... });
dc2.on('timeout', function(err, data) { ... });
dc2.on('done', function(errs, data) {
// errs - hash of errors
// data - hash of successfully received data
});
FYI: @aqrln, @belochub, @DzyubSpirit
Use case:
metasync.map(['Beijing', 'Roma', 'Kiev'], (item, callback) => {
getCityPopulation(item, (count) => {
callback(count);
});
}, (err, arr) => {
let total = arr.reduce((prev, cur) => (prev + cur), 0);
console.log('Total population: ' + total);
});
In 88#discussion_r115333601 @aqrln noticed that collector now increments counter even on same keys. On the one hand it's lightweight but on the other it may lead to errors in applied code. So I'd prefer to have both implementation:
metasync.collect(3)
metasync.collect(3).distinct()
Now callbacks in metasync.filter
and metasync.find
have single argument (result or data). I think it will be good to use common convention with (err, data)
. FYI: @aqrln
https://github.com/metarhia/MetaSync/blob/master/metasync.js#L141
I think should be simply:
clearTimeout(this.timer);
var t1 = metasync.throttle(5000, func);
t1(); t1(); t1(); // single call
setTimeout(t1, 7000); // another call
Callback will be optional
metasync
.for(data)
.map(fn1)
.fetch((err, data, [cb]) => cb(err, data)) // call cb to resume chain
.map(fn2)
.fetch((err, data) => console.log(data));
KeyCollector's constructor and collect
method have pretty same code with DataCollector. I think it's good to abstract it
Here is an idea of small but useful callback wrapper.
After applying metasync.cb
:
if (callback) return callback(err, data)
;const doSomethingAsync = (par1, parN, callback) => {
const cb = metasync.cb(callback); // apply wrapper
// ... some code here ...
// No need to check: if (cb) return ....
cb(err, data); // first call will be passed to callback
return cb(err, data); // second will write warning to console or log
};
Use case:
metasync.reduce(['Beijing', 'Roma', 'Kiev'], (prev, cur, callback) => {
getCityPopulation(cur, (count) => {
callback(prev + count);
});
}, (err, count) => {
console.log('Total population: ' + count);
}, 0);
var keyCollector = new metasync.KeyCollector(
['user', 'config', 'readme', 'timer'],
function(data) {
console.dir(Object.keys(data));
console.log('Collector test done');
end('KeyCollector');
}
);
keyCollector.collect('user', { name: 'Marcus Aurelius' });
fs.readFile('HISTORY.md', function(err, data) {
keyCollector.collect('history', data);
});
fs.readFile('README.md', function(err, data) {
keyCollector.collect('readme', data);
});
setTimeout(function() {
keyCollector.collect('timer', { date: new Date() });
}, ASYNC_TIMEOUT);
var queue = new metasync.ConcurrentQueue(concurrency, timeout);
queue.add({item});
queue.on('empty', fn);
queue.on('process', fn(item, callback));
queue.pause();
queue.resume();
queue.stop();
Here we have 5 branches:
process.nextTick
to fire chain in chain-alternative-nexttick.fire()
to fire chain in chain-alternative-fireI'm afraid that promises will affect performance and they may be not so effectivу.
I think they also use something like nextTick
to fire chain.
That's because I like manual .fire()
What do you think @aqrln ?
To prevent RangeError: Maximum call stack size exceeded
and break recursion we need setImmediate
in metasync.series
options: {
expected: 4, // expected data pices for DataCollector
timeout: 5000, // global timeout
each: 1000, // timeout for each function
error: { // TODO: invent better options structure
finish: true // finish on error if true
},
on: {
done: function(err, data) { // on finish
},
timeout: function(err, data) { // on timeout
}
}
}
Asynchronous implementation of Array.prototype.reduceRight()
Asynchronous implementation of Array.prototype.some()
const dc1 = metasync
.collect(3)
.timeout(5000)
.done((err, data) => {});
dc1(item);
const dc2 = metasync
.collect(['key1', 'key2', 'key3'])
.timeout(5000)
.done((err, data) => {});
dc2(key, value);
WDYT, @aqrln about .timeout(number)
and firing .catch on timeout ?
metasync.each(
['a', 'b', 'c'],
function iterator(item) { callback(); },
function done(data) { ... }
);
You can add more
We have ConcurrentQueue
in lib/queue.js
but new chaining metasync.queue
may implement more features from Queueing theory:
const cq = metasync.queue(3) // concurrency
.wait(2000) // task may wait in queue not more then 2 sec
.timeout(5000) // task processing timeout 5 sec
.throttle(100, 1000) // throttle processing to 100 rps
.process((item, cb) => cb(err, result)) // task processing function
.success((item) => {}) // on success
.failure((item) => {}) // on fail
.drain(() => {}); // on drain
@aqrln @belochub @nechaido @DzyubSpirit maybe you need more in JSTP or can add something for GlobalStorage or Impress use cases?
As we mentioned in #17 it would be great to have better syntax, chaining:
metasync.for(items).each(fn).fetch(fn);
metasync.for(items).reduce(fn).fetch(fn);
metasync.for(items).filter(fn).map(fn).then(fn).reduce(fn).fetch(fn);
Both DataCollector and KeyCollector initialize field errs
as empty arrays.
https://github.com/metarhia/MetaSync/blob/master/lib/collectors.js#L38
https://github.com/metarhia/MetaSync/blob/master/lib/collectors.js#L85
Also they use length field like errs
is array.
https://github.com/metarhia/MetaSync/blob/master/lib/collectors.js#L69
https://github.com/metarhia/MetaSync/blob/master/lib/collectors.js#L115
However adding errors is like errs
is object.
https://github.com/metarhia/MetaSync/blob/master/lib/collectors.js#L62
https://github.com/metarhia/MetaSync/blob/master/lib/collectors.js#L108
I think a right way is use errs
as object everywhere
Maybe it is better to have consistent function signature in chaining.
Now in following example fn1
and fn2
have different signature then fn3
and fn4
:
api.metasync.for(array).filter(fn1).map(fn2).then(fn3).catch(fn4);
fn1
and fn2
is: (element, callback) => callback(error, result)
fn3
is: (data) => {}
fn4
is: (error) => {}
.then()
and .catch()
into .done()
api.metasync.for(array).filter(fn1).map(fn2).done(fn3);
Signature for fn1
and fn2
will be: (element, callback) => callback(err, result)
But here is 2 alternative syntax for f3
:
(data, callback) => callback(error, data)
where result may be instance of Error or data(error, data, callback) => callback(error, data)
callback
in .done()
because chaining may contain multiple steps after .done()
api.metasync.for(array).filter(fn1).map(fn2).done(fn3).reduce(fn4).done(fn5);
@aqrln what do you think?
Asynchronous implementation of Array.prototype.every()
metasync.series(
['a', 'b', 'c'],
function iterator(item) { callback(); },
function done(data) { ... }
);
metasync.map
doesn't call done
function when is called with empty array. I think it's good to call done immediately instead of not calling at all.
metasync.composition(
[f1,f2,f3,[[f4,f5,[f6,f7],f8]],f9], // functional composition syntax
function done(data) { ... } // done
);
Refactor and remove old Node.js support.
MetaSync will support just Node.js 6.x and 7.x
Callback done
must not be called more then once per async primitive: each, series, ...
Function metasync.filter(items, iterator, done);
is an asynchronous analogue for synchronous Array.filter
['then', 'catch', 'map', 'filter', 'reduce', 'each', 'series', 'find']
on library load
Tests for metasync.filter
and metasync.find
are broken due to missing err
argument in callback.
Now tests silently failing, #77
metasync.every
- find all that satisfies passed function
metasync.find
- find first that satisfies passed function
In .map()
and .filter()
we use .forEach
but we can't break on error, so better to iterate with loops.
Function metasync.find(items, iterator, done);
is an asynchronous analogue for synchronous Array.find
We have totally broken metasync.find
, I tested it writing chaining test
This wrapper needed even inside common
so to avoid recursive dependency is will be good to move it from metasync.cb
to common.cb
. Mark metasync.cb
as deprecated and support it in [email protected]
. We will remove support in [email protected]
.
Now .fetch
can start chain ececution
Now it use push and sort. It is better to allocate new Array(len)
and remove filtered. But tests needed.
callback(err, data)
stop
everything if function returns error (optional)A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.