Attaching an attribute to a Promise

Viewed 1851

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 );

3 Answers
Related