node: allow multiple arguments passed to nextTick
authorTrevor Norris <trev.norris@gmail.com>
Thu, 5 Mar 2015 21:07:27 +0000 (14:07 -0700)
committerTrevor Norris <trev.norris@gmail.com>
Wed, 15 Apr 2015 23:02:21 +0000 (17:02 -0600)
PR-URL: https://github.com/iojs/io.js/pull/1077
Reviewed-by: Colin Ihrig <cjihrig@gmail.com>
18 files changed:
doc/api/process.markdown
lib/_debugger.js
lib/_http_client.js
lib/_http_outgoing.js
lib/_stream_duplex.js
lib/_stream_readable.js
lib/_stream_writable.js
lib/_tls_legacy.js
lib/child_process.js
lib/cluster.js
lib/dgram.js
lib/dns.js
lib/fs.js
lib/net.js
lib/zlib.js
src/node.js
test/message/stdin_messages.out
test/parallel/test-next-tick.js

index 8f22b3d..004958e 100644 (file)
@@ -687,7 +687,7 @@ This will generate:
 `heapTotal` and `heapUsed` refer to V8's memory usage.
 
 
-## process.nextTick(callback)
+## process.nextTick(callback[, arg][, ...])
 
 * `callback` {Function}
 
index 0517ed0..f1885b2 100644 (file)
@@ -587,9 +587,7 @@ Client.prototype.mirrorObject = function(handle, depth, cb) {
   } else {
     val = handle;
   }
-  process.nextTick(function() {
-    cb(null, val);
-  });
+  process.nextTick(cb, null, val);
 };
 
 
index 82bab78..daa37ef 100644 (file)
@@ -166,11 +166,8 @@ ClientRequest.prototype._implicitHeader = function() {
 };
 
 ClientRequest.prototype.abort = function() {
-  var self = this;
   if (this.aborted === undefined) {
-    process.nextTick(function() {
-      self.emit('abort');
-    });
+    process.nextTick(emitAbortNT, this);
   }
   // Mark as aborting so we can avoid sending queued request data
   // This is used as a truthy flag elsewhere. The use of Date.now is for
@@ -194,6 +191,11 @@ ClientRequest.prototype.abort = function() {
 };
 
 
+function emitAbortNT(self) {
+  self.emit('abort');
+}
+
+
 function createHangUpError() {
   var error = new Error('socket hang up');
   error.code = 'ECONNRESET';
@@ -440,12 +442,14 @@ function responseOnEnd() {
     socket.removeListener('error', socketErrorListener);
     // Mark this socket as available, AFTER user-added end
     // handlers have a chance to run.
-    process.nextTick(function() {
-      socket.emit('free');
-    });
+    process.nextTick(emitFreeNT, socket);
   }
 }
 
+function emitFreeNT(socket) {
+  socket.emit('free');
+}
+
 function tickOnSocket(req, socket) {
   var parser = parsers.alloc();
   req.socket = socket;
@@ -478,18 +482,18 @@ function tickOnSocket(req, socket) {
 }
 
 ClientRequest.prototype.onSocket = function(socket) {
-  var req = this;
-
-  process.nextTick(function() {
-    if (req.aborted) {
-      // If we were aborted while waiting for a socket, skip the whole thing.
-      socket.emit('free');
-    } else {
-      tickOnSocket(req, socket);
-    }
-  });
+  process.nextTick(onSocketNT, this, socket);
 };
 
+function onSocketNT(req, socket) {
+  if (req.aborted) {
+    // If we were aborted while waiting for a socket, skip the whole thing.
+    socket.emit('free');
+  } else {
+    tickOnSocket(req, socket);
+  }
+}
+
 ClientRequest.prototype._deferToConnect = function(method, arguments_, cb) {
   // This function is for calls that need to happen once the socket is
   // connected and writable. It's an important promisy thing for all the socket
index 3102261..8859055 100644 (file)
@@ -406,14 +406,9 @@ Object.defineProperty(OutgoingMessage.prototype, 'headersSent', {
 
 
 OutgoingMessage.prototype.write = function(chunk, encoding, callback) {
-  var self = this;
-
   if (this.finished) {
     var err = new Error('write after end');
-    process.nextTick(function() {
-      self.emit('error', err);
-      if (callback) callback(err);
-    });
+    process.nextTick(writeAfterEndNT, this, err, callback);
 
     return true;
   }
@@ -455,11 +450,7 @@ OutgoingMessage.prototype.write = function(chunk, encoding, callback) {
 
       if (this.connection && !this.connection.corked) {
         this.connection.cork();
-        var conn = this.connection;
-        process.nextTick(function connectionCork() {
-          if (conn)
-            conn.uncork();
-        });
+        process.nextTick(connectionCorkNT, this.connection);
       }
       this._send(len.toString(16), 'binary', null);
       this._send(crlf_buf, null, null);
@@ -475,6 +466,18 @@ OutgoingMessage.prototype.write = function(chunk, encoding, callback) {
 };
 
 
+function writeAfterEndNT(self, err, callback) {
+  self.emit('error', err);
+  if (callback) callback(err);
+}
+
+
+function connectionCorkNT(conn) {
+  if (conn)
+    conn.uncork();
+}
+
+
 OutgoingMessage.prototype.addTrailers = function(headers) {
   this._trailer = '';
   var keys = Object.keys(headers);
index 159e1f2..bf498f6 100644 (file)
@@ -49,5 +49,9 @@ function onend() {
 
   // no more data can be written.
   // But allow more writes to happen in this tick.
-  process.nextTick(this.end.bind(this));
+  process.nextTick(onEndNT, this);
+}
+
+function onEndNT(self) {
+  self.end();
 }
index bf73647..5600218 100644 (file)
@@ -395,9 +395,7 @@ function emitReadable(stream) {
     debug('emitReadable', state.flowing);
     state.emittedReadable = true;
     if (state.sync)
-      process.nextTick(function() {
-        emitReadable_(stream);
-      });
+      process.nextTick(emitReadable_, stream);
     else
       emitReadable_(stream);
   }
@@ -419,9 +417,7 @@ function emitReadable_(stream) {
 function maybeReadMore(stream, state) {
   if (!state.readingMore) {
     state.readingMore = true;
-    process.nextTick(function() {
-      maybeReadMore_(stream, state);
-    });
+    process.nextTick(maybeReadMore_, stream, state);
   }
 }
 
@@ -667,11 +663,7 @@ Readable.prototype.on = function(ev, fn) {
       state.emittedReadable = false;
       state.needReadable = true;
       if (!state.reading) {
-        var self = this;
-        process.nextTick(function() {
-          debug('readable nexttick read 0');
-          self.read(0);
-        });
+        process.nextTick(nReadingNextTick, this);
       } else if (state.length) {
         emitReadable(this, state);
       }
@@ -682,6 +674,11 @@ Readable.prototype.on = function(ev, fn) {
 };
 Readable.prototype.addListener = Readable.prototype.on;
 
+function nReadingNextTick(self) {
+  debug('readable nexttick read 0');
+  self.read(0);
+}
+
 // pause() and resume() are remnants of the legacy readable stream API
 // If the user uses them, then switch into old mode.
 Readable.prototype.resume = function() {
@@ -697,9 +694,7 @@ Readable.prototype.resume = function() {
 function resume(stream, state) {
   if (!state.resumeScheduled) {
     state.resumeScheduled = true;
-    process.nextTick(function() {
-      resume_(stream, state);
-    });
+    process.nextTick(resume_, stream, state);
   }
 }
 
@@ -883,13 +878,15 @@ function endReadable(stream) {
 
   if (!state.endEmitted) {
     state.ended = true;
-    process.nextTick(function() {
-      // Check that we didn't get one last unshift.
-      if (!state.endEmitted && state.length === 0) {
-        state.endEmitted = true;
-        stream.readable = false;
-        stream.emit('end');
-      }
-    });
+    process.nextTick(endReadableNT, state, stream);
+  }
+}
+
+function endReadableNT(state, stream) {
+  // Check that we didn't get one last unshift.
+  if (!state.endEmitted && state.length === 0) {
+    state.endEmitted = true;
+    stream.readable = false;
+    stream.emit('end');
   }
 }
index c02b773..da65ddb 100644 (file)
@@ -158,9 +158,7 @@ function writeAfterEnd(stream, cb) {
   var er = new Error('write after end');
   // TODO: defer error events consistently everywhere, not just the cb
   stream.emit('error', er);
-  process.nextTick(function() {
-    cb(er);
-  });
+  process.nextTick(cb, er);
 }
 
 // If we get something that is not a buffer, string, null, or undefined,
@@ -178,9 +176,7 @@ function validChunk(stream, state, chunk, cb) {
       !state.objectMode) {
     var er = new TypeError('Invalid non-string/buffer chunk');
     stream.emit('error', er);
-    process.nextTick(function() {
-      cb(er);
-    });
+    process.nextTick(cb, er);
     valid = false;
   }
   return valid;
@@ -298,10 +294,7 @@ function doWrite(stream, state, writev, len, chunk, encoding, cb) {
 
 function onwriteError(stream, state, sync, er, cb) {
   if (sync)
-    process.nextTick(function() {
-      state.pendingcb--;
-      cb(er);
-    });
+    process.nextTick(onwriteErrorNT, state, cb, er);
   else {
     state.pendingcb--;
     cb(er);
@@ -311,6 +304,11 @@ function onwriteError(stream, state, sync, er, cb) {
   stream.emit('error', er);
 }
 
+function onwriteErrorNT(state, cb, er) {
+  state.pendingcb--;
+  cb(er);
+}
+
 function onwriteStateUpdate(state) {
   state.writing = false;
   state.writecb = null;
@@ -339,9 +337,7 @@ function onwrite(stream, er) {
     }
 
     if (sync) {
-      process.nextTick(function() {
-        afterWrite(stream, state, finished, cb);
-      });
+      process.nextTick(afterWrite, stream, state, finished, cb);
     } else {
       afterWrite(stream, state, finished, cb);
     }
index fc0d115..3348d7f 100644 (file)
@@ -448,17 +448,19 @@ CryptoStream.prototype.destroy = function(err) {
   }
   this._opposite.destroy();
 
-  var self = this;
-  process.nextTick(function() {
-    // Force EOF
-    self.push(null);
-
-    // Emit 'close' event
-    self.emit('close', err ? true : false);
-  });
+  process.nextTick(destroyNT, this, err);
 };
 
 
+function destroyNT(self, err) {
+  // Force EOF
+  self.push(null);
+
+  // Emit 'close' event
+  self.emit('close', err ? true : false);
+}
+
+
 CryptoStream.prototype._done = function() {
   this._doneFlag = true;
 
@@ -667,8 +669,6 @@ function SecurePair(context, isServer, requestCert, rejectUnauthorized,
                           options);
   }
 
-  var self = this;
-
   options || (options = {});
 
   events.EventEmitter.call(this);
@@ -737,23 +737,25 @@ function SecurePair(context, isServer, requestCert, rejectUnauthorized,
   this.cleartext.init();
   this.encrypted.init();
 
-  process.nextTick(function() {
-    /* The Connection may be destroyed by an abort call */
-    if (self.ssl) {
-      self.ssl.start();
-
-      if (options.requestOCSP)
-        self.ssl.requestOCSP();
-
-      /* In case of cipher suite failures - SSL_accept/SSL_connect may fail */
-      if (self.ssl && self.ssl.error)
-        self.error();
-    }
-  });
+  process.nextTick(securePairNT, this, options);
 }
 
 util.inherits(SecurePair, events.EventEmitter);
 
+function securePairNT(self, options) {
+  /* The Connection may be destroyed by an abort call */
+  if (self.ssl) {
+    self.ssl.start();
+
+    if (options.requestOCSP)
+      self.ssl.requestOCSP();
+
+    /* In case of cipher suite failures - SSL_accept/SSL_connect may fail */
+    if (self.ssl && self.ssl.error)
+      self.error();
+  }
+}
+
 
 exports.createSecurePair = function(context,
                                     isServer,
@@ -835,12 +837,7 @@ exports.pipe = function pipe(pair, socket) {
   socket.pipe(pair.encrypted);
 
   pair.encrypted.on('close', function() {
-    process.nextTick(function() {
-      // Encrypted should be unpiped from socket to prevent possible
-      // write after destroy.
-      pair.encrypted.unpipe(socket);
-      socket.destroySoon();
-    });
+    process.nextTick(pipeCloseNT, pair, socket);
   });
 
   pair.fd = socket.fd;
@@ -886,3 +883,11 @@ exports.pipe = function pipe(pair, socket) {
 
   return cleartext;
 };
+
+
+function pipeCloseNT(pair, socket) {
+  // Encrypted should be unpiped from socket to prevent possible
+  // write after destroy.
+  pair.encrypted.unpipe(socket);
+  socket.destroySoon();
+}
index 0fdbcf4..99da7cf 100644 (file)
@@ -1015,9 +1015,7 @@ function ChildProcess() {
     // Do it on nextTick so that the user has one last chance
     // to consume the output, if for example they only want to
     // start reading the data once the process exits.
-    process.nextTick(function() {
-      flushStdio(self);
-    });
+    process.nextTick(flushStdio, self);
 
     maybeClose(self);
   };
@@ -1075,9 +1073,7 @@ ChildProcess.prototype.spawn = function(options) {
       err === uv.UV_EMFILE ||
       err === uv.UV_ENFILE ||
       err === uv.UV_ENOENT) {
-    process.nextTick(function() {
-      self._handle.onexit(err);
-    });
+    process.nextTick(onErrorNT, self, err);
     // There is no point in continuing when we've hit EMFILE or ENFILE
     // because we won't be able to set up the stdio file descriptors.
     // It's kind of silly that the de facto spec for ENOENT (the test suite)
@@ -1138,6 +1134,10 @@ ChildProcess.prototype.spawn = function(options) {
   return err;
 };
 
+function onErrorNT(self, err) {
+  self._handle.onexit(err);
+}
+
 
 ChildProcess.prototype.kill = function(sig) {
   var signal;
index 08230ad..10a5599 100644 (file)
@@ -241,9 +241,7 @@ function masterInit() {
     }
     cluster.settings = settings;
     if (initialized === true)
-      return process.nextTick(function() {
-        cluster.emit('setup', settings);
-      });
+      return process.nextTick(setupSettingsNT, settings);
     initialized = true;
     schedulingPolicy = cluster.schedulingPolicy;  // Freeze policy.
     assert(schedulingPolicy === SCHED_NONE || schedulingPolicy === SCHED_RR,
@@ -253,9 +251,7 @@ function masterInit() {
       return /^(--debug|--debug-brk)(=\d+)?$/.test(argv);
     });
 
-    process.nextTick(function() {
-      cluster.emit('setup', settings);
-    });
+    process.nextTick(setupSettingsNT, settings);
 
     // Send debug signal only if not started in debug mode, this helps a lot
     // on windows, because RegisterDebugHandler is not called when node starts
@@ -279,6 +275,10 @@ function masterInit() {
     });
   };
 
+  function setupSettingsNT(settings) {
+    cluster.emit('setup', settings);
+  }
+
   function createWorkerProcess(id, env) {
     var workerEnv = util._extend({}, process.env);
     var execArgv = cluster.settings.execArgv.slice();
@@ -376,13 +376,15 @@ function masterInit() {
     });
 
     worker.process.on('internalMessage', internal(worker, onmessage));
-    process.nextTick(function() {
-      cluster.emit('fork', worker);
-    });
+    process.nextTick(emitForkNT, worker);
     cluster.workers[worker.id] = worker;
     return worker;
   };
 
+  function emitForkNT(worker) {
+    cluster.emit('fork', worker);
+  }
+
   cluster.disconnect = function(cb) {
     var workers = Object.keys(cluster.workers);
     if (workers.length === 0) {
index 4708f4d..7d25928 100644 (file)
@@ -321,16 +321,19 @@ Socket.prototype.send = function(buffer,
                                   !!callback);
       if (err && callback) {
         // don't emit as error, dgram_legacy.js compatibility
-        process.nextTick(function() {
-          var ex = exceptionWithHostPort(err, 'send', address, port);
-          callback(ex);
-        });
+        process.nextTick(sendEmitErrorNT, err, address, port, callback);
       }
     }
   });
 };
 
 
+function sendEmitErrorNT(err, address, port, callback) {
+  var ex = exceptionWithHostPort(err, 'send', address, port);
+  callback(ex);
+}
+
+
 function afterSend(err) {
   if (err) {
     err = exceptionWithHostPort(err, 'send', this.address, this.port);
@@ -347,14 +350,17 @@ Socket.prototype.close = function(callback) {
   this._handle.close();
   this._handle = null;
   var self = this;
-  process.nextTick(function() {
-    self.emit('close');
-  });
+  process.nextTick(socketCloseNT, self);
 
   return this;
 };
 
 
+function socketCloseNT(self) {
+  self.emit('close');
+}
+
+
 Socket.prototype.address = function() {
   this._healthCheck();
 
index d39eec8..9334800 100644 (file)
@@ -61,15 +61,17 @@ function makeAsync(callback) {
       // The API already returned, we can invoke the callback immediately.
       callback.apply(null, arguments);
     } else {
-      var args = arguments;
-      process.nextTick(function() {
-        callback.apply(null, args);
-      });
+      process.nextTick(callMakeAsyncCbNT, callback, arguments);
     }
   };
 }
 
 
+function callMakeAsyncCbNT(callback, args) {
+  callback.apply(null, args);
+}
+
+
 function onlookup(err, addresses) {
   if (err) {
     return this.callback(errnoException(err, 'getaddrinfo', this.hostname));
index 0025e6a..b6b6226 100644 (file)
--- a/lib/fs.js
+++ b/lib/fs.js
@@ -91,14 +91,16 @@ function nullCheck(path, callback) {
     er.code = 'ENOENT';
     if (typeof callback !== 'function')
       throw er;
-    process.nextTick(function() {
-      callback(er);
-    });
+    process.nextTick(nullCheckCallNT, callback, er);
     return false;
   }
   return true;
 }
 
+function nullCheckCallNT(callback, er) {
+  callback(er);
+}
+
 // Static method to set the stats properties on a Stats object.
 fs.Stats = function(
     dev,
index ac6acd6..0865e9a 100644 (file)
@@ -267,12 +267,14 @@ function writeAfterFIN(chunk, encoding, cb) {
   // TODO: defer error events consistently everywhere, not just the cb
   self.emit('error', er);
   if (typeof cb === 'function') {
-    process.nextTick(function() {
-      cb(er);
-    });
+    process.nextTick(writeAfterFINNT, cb, er);
   }
 }
 
+function writeAfterFINNT(cb, er) {
+  cb(er);
+}
+
 exports.Socket = Socket;
 exports.Stream = Socket; // Legacy naming.
 
@@ -923,13 +925,7 @@ Socket.prototype.connect = function(options, cb) {
         // immediately calls net.Socket.connect() on it (that's us).
         // There are no event listeners registered yet so defer the
         // error event to the next tick.
-        process.nextTick(function() {
-          err.host = options.host;
-          err.port = options.port;
-          err.message = err.message + ' ' + options.host + ':' + options.port;
-          self.emit('error', err);
-          self._destroy();
-        });
+        process.nextTick(connectErrorNT, self, err, options);
       } else {
         self._unrefTimer();
         connect(self,
@@ -945,6 +941,15 @@ Socket.prototype.connect = function(options, cb) {
 };
 
 
+function connectErrorNT(self, err, options) {
+  err.host = options.host;
+  err.port = options.port;
+  err.message = err.message + ' ' + options.host + ':' + options.port;
+  self.emit('error', err);
+  self._destroy();
+}
+
+
 Socket.prototype.ref = function() {
   if (this._handle)
     this._handle.ref();
@@ -1183,14 +1188,17 @@ Server.prototype._listen2 = function(address, port, addressType, backlog, fd) {
   if (this._unref)
     this.unref();
 
-  process.nextTick(function() {
-    // ensure handle hasn't closed
-    if (self._handle)
-      self.emit('listening');
-  });
+  process.nextTick(emitListeningNT, self);
 };
 
 
+function emitListeningNT(self) {
+  // ensure handle hasn't closed
+  if (self._handle)
+    self.emit('listening');
+}
+
+
 function listen(self, address, port, addressType, backlog, fd, exclusive) {
   exclusive = !!exclusive;
 
index 48bb70c..4dd8b30 100644 (file)
@@ -455,12 +455,13 @@ Zlib.prototype.close = function(callback) {
 
   this._handle.close();
 
-  var self = this;
-  process.nextTick(function() {
-    self.emit('close');
-  });
+  process.nextTick(emitCloseNT, this);
 };
 
+function emitCloseNT(self) {
+  self.emit('close');
+}
+
 Zlib.prototype._transform = function(chunk, encoding, cb) {
   var flushFlag;
   var ws = this._writableState;
index 1339fb3..e4167cb 100644 (file)
           callback = tock.callback;
           threw = true;
           try {
-            callback();
+            if (tock.args === undefined)
+              callback();
+            else
+              callback.apply(null, tock.args);
             threw = false;
           } finally {
             if (threw)
             domain.enter();
           threw = true;
           try {
-            callback();
+            if (tock.args === undefined)
+              callback();
+            else
+              callback.apply(null, tock.args);
             threw = false;
           } finally {
             if (threw)
       } while (tickInfo[kLength] !== 0);
     }
 
-    function TickObject(c) {
+    function TickObject(c, args) {
       this.callback = c;
       this.domain = process.domain || null;
+      this.args = args;
     }
 
     function nextTick(callback) {
       if (process._exiting)
         return;
 
-      nextTickQueue.push(new TickObject(callback));
+      var args = undefined;
+      if (arguments.length > 1) {
+        args = [];
+        for (var i = 1; i < arguments.length; i++)
+          args.push(arguments[i]);
+      }
+
+      nextTickQueue.push(new TickObject(callback, args));
       tickInfo[kLength]++;
     }
 
index 5c0d86a..cbdc1fd 100644 (file)
@@ -11,7 +11,7 @@ SyntaxError: Strict mode code may not include a with statement
     at Socket.<anonymous> (node.js:*:*)
     at emitNone (events.js:*:*)
     at Socket.emit (events.js:*:*)
-    at _stream_readable.js:*:*
+    at endReadableNT (_stream_readable.js:*:*)
     at process._tickCallback (node.js:*:*)
 42
 42
@@ -28,7 +28,7 @@ Error: hello
     at Socket.<anonymous> (node.js:*:*)
     at emitNone (events.js:*:*)
     at Socket.emit (events.js:*:*)
-    at _stream_readable.js:*:*
+    at endReadableNT (_stream_readable.js:*:*)
     at process._tickCallback (node.js:*:*)
 
 [stdin]:1
@@ -43,7 +43,7 @@ Error: hello
     at Socket.<anonymous> (node.js:*:*)
     at emitNone (events.js:*:*)
     at Socket.emit (events.js:*:*)
-    at _stream_readable.js:*:*
+    at endReadableNT (_stream_readable.js:*:*)
     at process._tickCallback (node.js:*:*)
 100
 
@@ -59,7 +59,7 @@ ReferenceError: y is not defined
     at Socket.<anonymous> (node.js:*:*)
     at emitNone (events.js:*:*)
     at Socket.emit (events.js:*:*)
-    at _stream_readable.js:*:*
+    at endReadableNT (_stream_readable.js:*:*)
     at process._tickCallback (node.js:*:*)
 
 [stdin]:1
index f8b5fed..1081a96 100644 (file)
@@ -23,6 +23,13 @@ process.nextTick(function() {
   complete++;
 });
 
+var obj = {};
+
+process.nextTick(function(a, b) {
+  assert.equal(a, 42);
+  assert.equal(b, obj);
+}, 42, obj);
+
 process.on('exit', function() {
   assert.equal(5, complete);
   process.nextTick(function() {