summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/api/timers.markdown17
-rw-r--r--lib/timers.js71
-rw-r--r--src/node.js10
-rw-r--r--test/common.js2
-rw-r--r--test/simple/test-timers-immediate.js53
5 files changed, 153 insertions, 0 deletions
diff --git a/doc/api/timers.markdown b/doc/api/timers.markdown
index c25ada14cf..a1e2d471a3 100644
--- a/doc/api/timers.markdown
+++ b/doc/api/timers.markdown
@@ -46,3 +46,20 @@ event loop performance -- use wisely.
If you had previously `unref()`d a timer you can call `ref()` to explicitly
request the timer hold the program open. If the timer is already `ref`d calling
`ref` again will have no effect.
+
+## setImmediate(callback, [arg], [...])
+
+To schedule the "immediate" execution of `callback`. Returns an `immediateId`
+for possible use with `clearImmediate()`. Optionally you can also pass
+arguments to the callback.
+
+Immediates are queued in the order created, and are popped off the queue once
+per loop iteration. This is different from `process.nextTick` which will
+execute `process.maxTickDepth` queued callbacks per iteration. `setImmediate`
+will yield to the event loop after firing a queued callback to make sure I/O is
+not being starved. While order is preserved for execution, other I/O events may
+fire between any two scheduled immediate callbacks.
+
+## clearImmediate(immediateId)
+
+Stops an immediate from triggering.
diff --git a/lib/timers.js b/lib/timers.js
index 03db989b11..2d50cbddec 100644
--- a/lib/timers.js
+++ b/lib/timers.js
@@ -277,3 +277,74 @@ Timeout.prototype.close = function() {
exports.unenroll(this);
}
};
+
+
+var immediateTimer = null;
+var immediateQueue = { started: false };
+L.init(immediateQueue);
+
+
+function lazyImmediateInit() { // what's in a name?
+ if (immediateTimer) return;
+ immediateTimer = new Timer;
+ immediateTimer.ontimeout = processImmediate;
+}
+
+
+function processImmediate() {
+ var immediate;
+ if (L.isEmpty(immediateQueue)) {
+ immediateTimer.stop();
+ immediateQueue.started = false;
+ } else {
+ immediate = L.shift(immediateQueue);
+
+ if (immediate.domain) immediate.domain.enter();
+
+ immediate._onTimeout();
+
+ if (immediate.domain) immediate.domain.exit();
+ }
+};
+
+
+exports.setImmediate = function(callback) {
+ var immediate = {}, args;
+
+ L.init(immediate);
+
+ immediate._onTimeout = callback;
+
+ if (arguments.length > 1) {
+ args = Array.prototype.slice.call(arguments, 1);
+ immediate._onTimeout = function() {
+ callback.apply(null, args);
+ };
+ }
+
+ if (!immediateQueue.started) {
+ lazyImmediateInit();
+ immediateTimer.start(0, 1);
+ immediateQueue.started = true;
+ }
+
+ if (process.domain) immediate.domain = process.domain;
+
+ L.append(immediateQueue, immediate);
+
+ return immediate;
+};
+
+
+exports.clearImmediate = function(immediate) {
+ if (!immediate) return;
+
+ immediate._onTimeout = undefined;
+
+ L.remove(immediate);
+
+ if (L.isEmpty(immediateQueue)) {
+ immediateTimer.stop();
+ immediateQueue.started = false;
+ }
+};
diff --git a/src/node.js b/src/node.js
index 848c39d119..07db3f0963 100644
--- a/src/node.js
+++ b/src/node.js
@@ -184,6 +184,16 @@
var t = NativeModule.require('timers');
return t.clearInterval.apply(this, arguments);
};
+
+ global.setImmediate = function() {
+ var t = NativeModule.require('timers');
+ return t.setImmediate.apply(this, arguments);
+ };
+
+ global.clearImmediate = function() {
+ var t = NativeModule.require('timers');
+ return t.clearImmediate.apply(this, arguments);
+ };
};
startup.globalConsole = function() {
diff --git a/test/common.js b/test/common.js
index e0ac7a09e1..62dd0f4a80 100644
--- a/test/common.js
+++ b/test/common.js
@@ -81,8 +81,10 @@ process.on('exit', function() {
if (!exports.globalCheck) return;
var knownGlobals = [setTimeout,
setInterval,
+ setImmediate,
clearTimeout,
clearInterval,
+ clearImmediate,
console,
Buffer,
process,
diff --git a/test/simple/test-timers-immediate.js b/test/simple/test-timers-immediate.js
new file mode 100644
index 0000000000..0bd8ae9642
--- /dev/null
+++ b/test/simple/test-timers-immediate.js
@@ -0,0 +1,53 @@
+// Copyright Joyent, Inc. and other Node contributors.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice shall be included
+// in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+var common = require('../common');
+var assert = require('assert');
+
+var immediateA = false,
+ immediateB = false,
+ immediateC = [],
+ before;
+
+setImmediate(function() {
+ try {
+ immediateA = process.hrtime(before);
+ } catch(e) {
+ console.log('failed to get hrtime with offset');
+ }
+ clearImmediate(immediateB);
+});
+
+before = process.hrtime();
+
+immediateB = setImmediate(function() {
+ immediateB = true;
+});
+
+setImmediate(function(x, y, z) {
+ immediateC = [x, y, z];
+}, 1, 2, 3);
+
+process.on('exit', function() {
+ assert.ok(immediateA, 'Immediate should happen after normal execution');
+ assert.notStrictEqual(immediateB, true, 'immediateB should not fire');
+ assert.deepEqual(immediateC, [1, 2, 3], 'immediateC args should match');
+});