Reputation: 9938
I am trying to make a small utility function toPromise
that transforms a function using callback into a promise function (using Q).
Here is the utility:
Object.defineProperty(Function.prototype, "toPromise", {
enumerable: false,
configurable: false,
writable: false,
value: function(self) {
var $this;
$this = this;
return function() {
var args, deferred;
deferred = Q.defer();
args = arguments;
args[args.length] = deferred.resolve;
console.dir(args);
$this.apply(self, args);
return deferred.promise;
};
}
});
Here is the usage:
var f = function(nickname, name, callback){
console.log(arguments)
callback(undefined, name+" "+nickname);
};
f.toPromise(this)("a", "b").then(function(str){
console.log(str)
});
But the output is:
{ '0': 'a', '1': 'b', '2': [Function] } // This is debugging from utils
{ '0': 'a', '1': 'b' } // and from the function f
C:\test.js:4
callback(undefined, name+" "+nickname);
^
TypeError: undefined is not a function
So why is the third arg (in utils), the function, not passed in the apply
?
Upvotes: 1
Views: 83
Reputation: 664599
You did assign the callback to the arguments
object, but since it's not an actual array that did not automatically increase its length
property. With length
still being 2
, your callback was simply skipped in the apply
.
The fix is simple:
Function.prototype.toPromise = function(self) {
var fn = this;
return function() {
var deferred = Q.defer();
arguments[arguments.length++] = deferred.resolve;
// ^^
fn.apply(self||this, arguments);
return deferred.promise;
};
};
You also might use Array.prototype.push.call(arguments, deferred.resolve)
. Notice that Q
already provides such similar1 a functionality (with probably optimized efficiency), so if you just want to use it on the prototype you'd better do
Function.prototype.toPromise = function(self) {
return Q.nbind(this, self);
};
1: Notice that resolve()
does not take an error argument, like "nodebacks" usually do
Upvotes: 2
Reputation: 72271
The arguments
object is not a real array:
>>> function getArguments() { return arguments; }
>>> var a = getArguments(1,2,3), b = [1,2,3];
>>> a[3] = 4; b[3] = 4;
>>> a
[1, 2, 3]
>>> b
[1, 2, 3, 4]
>>> a.length
3
>>> b.length
4
so if you intend to modify it or pass it around to outside the function it's good to convert it to a real array:
var args = Array.prototype.slice.call(arguments);
Upvotes: 2