Reputation: 5645
Another question on SO perked my interest in creating a setTimeout
that returned a Promise and a timerId, yet would not break backwards compatibility. This is the question I am referring to.
It simply asks if the inner return statement in the _.delay
method of underscore.js
is extraneous or serves a purpose. Here is the block of code.
This is the code for _.delay:
// Delays a function for the given number of milliseconds, and then calls
// it with the arguments supplied.
_.delay = function(func, wait) {
var args = slice.call(arguments, 2);
return setTimeout(function() { // this is to return the timerId
return func.apply(null, args); // this guy right is in question
}, wait);
};
Considering setTimeout does not currently return a Promise, I proposed the idea that it is possibly there for future proofing, incase setTimeout does one day return a promise.
To do this setTimeout
would need to return a timerId so that it can be canceled. So to return a Promise
you would need to attach the timerId to the Promise, so that the Promise is returned and the timerId is accessable.
You could then modify clearTimeout
to perform exactly how it does now when given a timerId, but for a Promise it uses Promise.timerId to clear the timeout and cancel the Promise. Of course Promise canceling would need to be implemented as well...
Anyhow... I started working on something for fun and came across something I haven't been able to explain. If you run the snippet below, you will see that the promise has .timerId
attribute before it is returned, but the attribute is missing after the return. Can anyone explain this?
function pseudoSetTimeout( func, wait ) {
let timerId,
promise = new Promise( ( resolve, reject ) => {
timerId = setTimeout( () => {
let returnVal = func();
resolve( returnVal );
}, wait );
});
promise.timerId = timerId;
console.log( "promise before return: ", promise );
return promise;
}
function callback() {
return "Callback fired";
}
let timeout = pseudoSetTimeout( callback, 1000 )
.then( ( val ) => {
console.log( val );
});
console.log( "returned promise: ", timeout );
Upvotes: 6
Views: 2471
Reputation: 13198
Due to the reasons mentioned in the other answers, I would return a 'tuple' instead of using a property on the promise:
function pseudoSetTimeout( func, wait ) {
let timerId,
promise = new Promise( ( resolve, reject ) => {
timerId = setTimeout( () => {
let returnVal = func();
resolve( returnVal );
}, wait );
});
let result = [timerId, promise];
console.log ( "returning: ", result );
return result;
}
function callback() {
return "Callback fired";
}
let [timeout, promise] = pseudoSetTimeout( callback, 1000 );
promise.then( ( val ) => {
console.log( val );
});
console.log( "returned timeout: ", timeout );
Upvotes: 0
Reputation: 351074
The then
in:
pseudoSetTimeout( callback, 1000 )
.then( ( val ) => {
console.log( val );
});
returns a new promise, which is not the one that pseudoSetTimeout
returned. It is a promise with undefined
as promised value, since the then
callback does not return anything.
You could make it work by not applying then
during the assignment:
function pseudoSetTimeout( func, wait ) {
let timerId,
promise = new Promise( ( resolve, reject ) => {
timerId = setTimeout( () => {
let returnVal = func();
resolve( returnVal );
}, wait );
});
promise.timerId = timerId;
console.log( "promise before return: ", promise );
return promise;
}
function callback() {
return "Callback fired";
}
let timeout = pseudoSetTimeout( callback, 1000 );
timeout.then( ( val ) => {
console.log( val );
});
console.log( "returned promise: ", timeout );
Because then
is crucial in the use of promises, and is often used in chaining them, it seems that the idea to attach a custom property to a promise loses on usefulness.
Upvotes: 5
Reputation: 3543
Really interesting question.
Not sure of how it's made internally in node, but if looking on the then code of bluebird is any indication : https://github.com/petkaantonov/bluebird/blob/master/src/thenables.js
My guess would be that the Promise returned is actually another object.
Upvotes: 0