Skip to content

Commit

Permalink
Merge branch 'soon' of git://github.com/solmsted/yui3 into solmsted-soon
Browse files Browse the repository at this point in the history
  • Loading branch information
davglass committed Jan 4, 2013
2 parents 0763dd7 + 6ffd57f commit 8bb1123
Show file tree
Hide file tree
Showing 7 changed files with 204 additions and 0 deletions.
3 changes: 3 additions & 0 deletions src/timers/HISTORY.md
@@ -0,0 +1,3 @@
timers
========
* Initial release.
6 changes: 6 additions & 0 deletions src/timers/README.md
@@ -0,0 +1,6 @@
timers
========
Similar to `Y.later`, but sooner. `Y.soon` standardizes the way to make
something happen asynchronously but without a timed delay. Under the covers,
`Y.soon` will call `setTimeout` if it must, but it will try to use the more
efficient `setImmediate` or `process.nextTick` depending on the environment.
10 changes: 10 additions & 0 deletions src/timers/build.json
@@ -0,0 +1,10 @@
{
"builds": {
"timers": {
"jsfiles": [
"js/timers.js"
]
}
},
"name": "timers"
}
89 changes: 89 additions & 0 deletions src/timers/js/timers.js
@@ -0,0 +1,89 @@
/**
* Provides utilities for timed asynchronous callback execution.
* Y.soon is a setImmediate/process.nextTick/setTimeout wrapper.
* @module timers
* @author Steven Olmsted
*/

var global = Y.config.global,

/**
* Y.soon accepts a callback function. The callback function will be called
* once in a future turn of the JavaScript event loop. If the function
* requires a specific execution context or arguments, wrap it with Y.bind.
* Y.soon returns an object with a cancel method. If the cancel method is
* called before the callback function, the callback function won't be
* called.
* @method soon
* @for YUI
* @param {Function} callbackFunction
* @return {Object} An object with a cancel method. If the cancel method is
* called before the callback function, the callback function won't be
* called.
*/
soon = function (callbackFunction) {
var canceled;

soon._asynchronizer(function () {
// Some asynchronizers may provide their own cancellation
// methods such as clearImmediate or clearTimeout but some
// asynchronizers do not. For simplicity, cancellation is
// entirely handled here rather than wrapping the other methods.
// All asynchronizers are expected to always call this anonymous
// function.
if (!canceled) {
callbackFunction();
}
});

return {
cancel: function () {
canceled = 1;
}
};
};

/**
* The asynchronizer is the internal mechanism which will call a function
* asynchronously. This property is exposed as a convenient way to define a
* different asynchronizer implementation without having to rewrite the
* entire Y.soon interface.
* @method _asynchronizer
* @for soon
* @param {Function} callbackFunction The function to call asynchronously.
* @protected
*/

/**
* Since Y.soon is likely to have many differing asynchronizer
* implementations, this property should be set to identify which
* implementation is in use.
* @property _impl
* @protected
* @type String
*/

// Check for a native or already polyfilled implementation of setImmediate.
if ('setImmediate' in global) {
soon._asynchronizer = function (callbackFunction) {
setImmediate(callbackFunction);
};
soon._impl = 'setImmediate';
}

// Check for process and process.nextTick
else if (('process' in global) && ('nextTick' in process)) {
soon._asynchronizer = process.nextTick;
soon._impl = 'nextTick';
}

// The most widely supported asynchronizer is setTimeout so we use that as
// the fallback.
else {
soon._asynchronizer = function (callbackFunction) {
setTimeout(callbackFunction, 0);
};
soon._impl = 'setTimeout';
}

Y.soon = soon;
7 changes: 7 additions & 0 deletions src/timers/meta/timers.json
@@ -0,0 +1,7 @@
{
"timers": {
"requires": [
"yui-base"
]
}
}
29 changes: 29 additions & 0 deletions src/timers/tests/unit/index.html
@@ -0,0 +1,29 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<title>
timers
</title>
<script src="/build/yui/yui.js">
</script>
<script src="js/tests.js">
</script>
</head>
<body class="yui3-skin-sam">
<div id="logger">
</div>
<script>
YUI({
coverage: [
'timers'
],
filter: (window.location.search.match(/[?&]filter=([^&]+)/) || [])[1] || 'raw'
}).use('test-console', 'test', 'module-tests', function (Y) {
(new Y.Test.Console()).render('#logger');
Y.Test.Runner.setName('timers');
Y.Test.Runner.run();
});
</script>
</body>
</html>
60 changes: 60 additions & 0 deletions src/timers/tests/unit/js/tests.js
@@ -0,0 +1,60 @@
YUI.add('module-tests', function (Y) {
'use strict';

var suite = new Y.Test.Suite('soon');

suite.add(new Y.Test.Case({
name: 'Automated Tests',
'test:001-apiExists': function () {
Y.Assert.isFunction(Y.soon, 'Y.soon should be a function.');
Y.Assert.isFunction(Y.soon._asynchronizer, 'Y.soon._asynchronizer should be a function.');
Y.Assert.isString(Y.soon._impl, 'Y.soon._impl should be a string.');
},
'test:002-asyncCallbackFunction': function () {
var count = 0,
test = this,
timer = Y.soon(function () {
count += 1;

if (count > 1) {
test.resume(function () {
Y.Assert.fail('Y.soon() callback function should not execute multiple times.');
});
} else {
test.resume(function () {
// Arbitrary timeout to test that the callback
// function does not execute again.
test.wait(function () {
Y.Assert.isTrue(true);
}, 150);
});
}
});

Y.Assert.areSame(0, count);
Y.Assert.isObject(timer);
Y.Assert.isFunction(timer.cancel);

test.wait();
},
'test:003-cancel': function () {
var count = 0,
timer = Y.soon(function () {
count += 1;
});

timer.cancel();

this.wait(function () {
Y.Assert.areSame(0, count);
}, 250);
}
}));

Y.Test.Runner.add(suite);
}, '', {
requires: [
'test',
'timers'
]
});

0 comments on commit 8bb1123

Please sign in to comment.