Skip to content

Instantly share code, notes, and snippets.

@twolfson
Forked from 140bytes/LICENSE.txt
Created May 12, 2012 19:16
Show Gist options
  • Save twolfson/2668303 to your computer and use it in GitHub Desktop.
Save twolfson/2668303 to your computer and use it in GitHub Desktop.
Atomic event emitter for solving your race conditions

Atomic Event Emitter

Occasionally, you have a race condition between two asynchronous functions where one must run after the other. This atomic event emitter is built just for that.

var atomic = {s:{},after:function(n,f,c){c=this.s[n]=this.s[n]||[];c==1?f():c.push(f)},fire:function(n,c,f){for(c=this.s[n]||[];f=c.pop();)f();this.s[n]=1}};
setTimeout(function () {
  atomic.fire('first');
}, 100);
setTimeout(function () {
  atomic.after('first', function () {
    console.log('First must have run before this');
  });
}, 70 + (Math.random() * 60));

Tested in

This code is not tested anywhere (browsers and node).

{
// Define the channels
'channels': {},
'after': function (channelName, fn) {
// Fallback the channel
var channel = (this.channels[channelName] = this.channels[channelName] || []);
// If the channel has already been run, run the fn
if (channel === true) {
fn();
} else {
// Otherwise, add the function
channel.push(fn);
}
},
'fire': function (channelName) {
// Fallback the channel
var channel = (this.channels[channelName] || []),
i = channel.length;
// Iterate and call the functions
while (i--) {
channel[i]();
}
// Set up the channel for atomic bindings
this.channels[channelName] = true;
}
}
function(s,f,c){s={};return{after:function(n,f){c=s[n]=s[n]||[];c==1?f():c.push(f)},fire:function(n){for(c=s[n]||[];f=c.pop();)f();s[n]=1}}}
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
Version 2, December 2004
Copyright (C) 2011 YOUR_NAME_HERE <YOUR_URL_HERE>
Everyone is permitted to copy and distribute verbatim or modified
copies of this license document, and changing it is allowed as long
as the name is changed.
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. You just DO WHAT THE FUCK YOU WANT TO.
{
"name": "atomicEvent",
"description": "Atomic event emitter for solving your race conditions",
"keywords": [
"atomic",
"event",
"emitter",
"race",
"condition"
]
}
<!DOCTYPE html>
<title>Atomic Event</title>
<div>Expected value: <b>First must have run before this</b></div>
<div>Actual value: <b id="ret"></b></div>
<script>
// write a small example that shows off the API for your example
// and tests it in one fell swoop.
var atomic = (function(s,f,c){s={};return{after:function(n,f){c=s[n]=s[n]||[];c==1?f():c.push(f)},fire:function(n){for(c=s[n]||[];f=c.pop();)f();s[n]=1}}})();
setTimeout(function () {
window.first = true;
atomic.fire('first');
}, 100);
setTimeout(function () {
atomic.after('first', function () {
if (window.first === true) {
document.getElementById( "ret" ).innerHTML = 'First must have run before this';
}
});
}, 70 + (Math.random() * 60));
</script>
@atk
Copy link

atk commented May 14, 2012

I don't see how this example points out the functionality, as the result would be there in any case. You could set a data attribute to ret and check it before putting the message there. Otherwise, nice one.

@twolfson
Copy link
Author

Well that's the point right?
If the second timeout is < 100ms, the actual function is triggered by atomic after the first one runs.
If it is >= 100ms, it just runs immediately.
You are right though, there is no proof of sequence -- only proof that the second function runs. I will toss that in.

@maettig
Copy link

maettig commented May 18, 2012

You know, this doesn't really solve your problem. For example it's totally possible that push() is called after the channel was set to true. Or two calls of fire can call the function twice before the channel is set to true. However, nice snipped and nice style.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment