The current low-level async APIs have performance-hostile design as each level that passes a callback requires the allocation of a closre (which is in this case the allocation of 2 objects: JSFunction object and a Context object).
For example:
fs.doSomething = function(arg, callback) {
var req = new FSReqWrap();
req.oncomplete = callback;
binding.doSomething(arg, req);
};
110% of the time callback has some context it needs around once it's called, so it can never be a static function that is only allocated once per program. Also any higher level fs abstraction is going to allocate a closure for its own context and so on. These closures are relatively expensive objects, more expensive than e.g. a Promise object.
With context passing:
fs.doSomething = function(arg, callback) {
var req = new FSReqWrap();
req.oncomplete = callback;
// When the request completes, it would call req.oncomplete.call(req.context, err, result);
req.context = ...
binding.doSomething(arg, req);
}
This would enable extremely performant promise-wrappers (given user-land promises):
fs.doSomethingPromise = function(arg) {
var promise = new Promise(INTERNAL);
var req = new FSReqWrap();
req.oncomplete = promise._resolveFromNodeBack;
req.context = promise;
binding.doSomething(arg, req);
return promise;
};
The above code literally allocates less objects than a fs.doSomething(1, function closure(){}).
But it would also enable high level fs functions that are build on top of low level fs functions to allocate less closures.
Another example of context-passing include most of the ES5 array methods.
The current low-level async APIs have performance-hostile design as each level that passes a callback requires the allocation of a closre (which is in this case the allocation of 2 objects: JSFunction object and a Context object).
For example:
110% of the time
callbackhas some context it needs around once it's called, so it can never be a static function that is only allocated once per program. Also any higher level fs abstraction is going to allocate a closure for its own context and so on. These closures are relatively expensive objects, more expensive than e.g. a Promise object.With context passing:
This would enable extremely performant promise-wrappers (given user-land promises):
The above code literally allocates less objects than a
fs.doSomething(1, function closure(){}).But it would also enable high level fs functions that are build on top of low level fs functions to allocate less closures.
Another example of context-passing include most of the ES5 array methods.