Last active
October 6, 2016 14:47
-
-
Save andrevinsky/f9a2b701e6c629ade182710541570386 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const KEYS = { | |
key: 'k', | |
invoked: 'i', | |
fulfilled: 'f', | |
depsOn: 'd', | |
notify: 'n' | |
}; | |
function ackProducer(handle, { hashMap = {}, lastKey = null } = {}, diag) { | |
const state = { hashMap, lastKey }; | |
return makeAck; | |
// or: | |
// return { makeAck, ack, getState } | |
function makeAck() { | |
const key = (arguments.length == 1) ? ('' + arguments[0]) : Math.random().toString().substr(2); | |
const dependsOn = state.lastKey; | |
state.lastKey = key; | |
state.hashMap[key] = { | |
[KEYS.key]: key, | |
[KEYS.invoked]: false, | |
[KEYS.fulfilled]: false, | |
[KEYS.depsOn]: dependsOn, | |
[KEYS.notify]: null | |
}; | |
diag && diag('Produced ack: ' + key, state); | |
return getAckForKey(key); | |
// or: | |
// return key; | |
} | |
function ack(key) { | |
return getAckForKey(key)(); | |
} | |
function getState() { | |
return state; | |
} | |
function getAckForKey(key) { | |
return function () { | |
diag && diag('Called ack: ' + key); | |
// no action if there isn't this node or it has been executed once | |
const node = keyToNode(key); | |
if (!node || node[KEYS.invoked]) { | |
return; | |
} | |
// need only mark it as invoked and attempt to handle the action | |
node[KEYS.invoked] = true; | |
diag && diag('Called & processing ack: ' + key, state); | |
maybeHandleKey(key); | |
}; | |
} | |
function maybeHandleKey(key) { | |
const node = keyToNode(key); // it is ensured to be there by all outer calls | |
// only these props matter here | |
const { | |
[KEYS.depsOn]: depsOn, // node this one depends on | |
[KEYS.notify]: notify // node that will be notified if this one gets fulfilled | |
} = node; | |
const depNode = keyToNode(depsOn); | |
// if the key exists and not the node this one depends on, | |
// we clean it out | |
if (depsOn && !depNode) { | |
node[KEYS.depsOn] = null; // cleaning | |
} | |
// if this node depends on no node or the one it depends on is fulfilled | |
// it means this node gets fulfilled as well | |
if (!depNode || depNode[KEYS.fulfilled]) { | |
node[KEYS.fulfilled] = true; | |
} | |
if (node[KEYS.fulfilled]) { | |
// if we determined this node is fulfilled | |
// we need to call a handler with the key | |
handle(key); | |
diag && diag('Fulfilling ack: ' + key, state); | |
// .. remove this node from the hashMap | |
delete state.hashMap[key]; | |
// and if there is a node to notify, we notify it | |
notify && maybeHandleKey(notify); | |
diag && diag('Fulfilled ack: ' + key, state); | |
} else { | |
// otherwise, we update the node this one depends on | |
// to notify this one once it gets fulfilled | |
if (depNode) { | |
depNode[KEYS.notify] = key; | |
} | |
diag && diag('Pending fulfilling ack: ' + key, state); | |
} | |
} | |
function keyToNode(key) { | |
return state.hashMap[key]; | |
} | |
} | |
function handle(type, key) { | |
console.log('Acked: T<' + type + '>: ' + key); | |
} | |
function diag (type, msg, { hashMap } = {}) { | |
// if (!hashMap) { | |
// return; | |
// } | |
// console.log(type, msg, JSON.stringify(hashMap || null)); | |
// const results = Object.keys(hashMap).map(k => hashMap[k]).filter(o => (o[KEYS.depsOn].length > 1) || (o[KEYS.notify].length > 1)) | |
// if (results.length) { | |
// console.warn(results); | |
// } | |
} | |
function timed(fn) { | |
return function() { | |
const startTime = new Date() - 0; | |
fn() | |
console.log('Execution took ' + (new Date() - startTime) + 'ms'); | |
} | |
} | |
const typeAcksGen = { | |
'type0': ackProducer(handle.bind(null, 'type0'), undefined, diag.bind(null, 'type0')), | |
'type1': ackProducer(handle.bind(null, 'type1'), undefined, diag.bind(null, 'type1')), | |
'type2': ackProducer(handle.bind(null, 'type2'), undefined, diag.bind(null, 'type2')) | |
}; | |
// const acksHere = ackProducer(handle, function(msg, state) { | |
// console.log(msg, JSON.stringify(state || null)); | |
// }); | |
timed(function(){ | |
let acks = Array(200).fill(0).map((v, k) => typeAcksGen['type' + (k % 3)](k)); | |
while (acks.length) { | |
const idx = Math.floor(Math.random() * acks.length); | |
const ack = acks.splice(idx, 1); | |
ack[0] && ack[0](); | |
} | |
})(); | |
// const ack1 = acksHere('1'); | |
// const ack2 = acksHere('2'); | |
// const ack3 = acksHere('3'); | |
// const ack4 = acksHere('4'); | |
// ack4(); | |
// const ack5 = acksHere('5'); | |
// ack2(); | |
// ack3(); | |
// ack1(); | |
// ack5(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment