Search code examples
node.jspromisees6-promise

How can I satisfy promises aplus spec 2.2.4 in NodeJS?


I am writing an HTTP Promises package for nodeJS. I need it to be promises aplus compliant. I am using promises-aplus-tests - https://www.npmjs.com/package/promises-aplus-tests

I have created an adapter as follows:

var adapter = {
        deferred: function() {
            var d = {}
            d.promise = new HTTPromise(new GETOptions('/path', DEFAULT_OPTIONS));
            d.resolve = executorResponseFunc;
            d.reject = executorRejectFunc;        
            return d;
        }
    }

promisesAplusTests(adapter, function (err) {
    done(err);
});

My tests are failing on https://promisesaplus.com/#point-34 ... with further help on https://promisesaplus.com/#point-67

2.2.4 onFulfilled or onRejected must not be called until the execution context stack contains only platform code

3.1 Here “platform code” means engine, environment, and promise implementation code. In practice, this requirement ensures that onFulfilled and onRejected execute asynchronously, after the event loop turn in which then is called, and with a fresh stack. This can be implemented with either a “macro-task” mechanism such as setTimeout or setImmediate, or with a “micro-task” mechanism such as MutationObserver or process.nextTick. Since the promise implementation is considered platform code, it may itself contain a task-scheduling queue or “trampoline” in which the handlers are called.

But I can't get it passing this promises aplus test. I am using the bog standard JavaScript Promises library (which on its own passes the tests) documented at ...

https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise

new Promise(executor);

new Promise(function(resolve, reject) { ... });

In the executor I have tried ...

if (response.statusCode === 200) {
    process.nextTick(() => {
        resolve(finalResponse);   
    });
} else {
    process.nextTick(() => {
        reject(finalResponse);
    });
}

and also ...

process.nextTick(() => {
    if (response.statusCode === 200) {
        resolve(finalResponse);      
    } else {
         reject(finalResponse);
    }
});

There seems to be a lot of talk on the Internet but not many code examples of how to comply. Is there a code sample on how to satisfy this test and how you would use nextTick or another suitable solution?

It's worthing mentioning I have another call to reject within an HTTP error handler within the executor function.


Solution

  • I would like to apologise to Bergi, I think I have the answer to my stupidity but I would appreciate confirmation that my thinking is now correct ...

    My initial confusion came from ... https://www.npmjs.com/package/request-promise

    I am writing a similar but more powerful npm tool. They have the "then" badge, so obviously I wanted it! I thought I needed to pass your tests (they have made some kind of attempt at it). So I tried to pass your promises-aplus-tests with my library, which uses promises heavily but is NOT a promise implementation.

    I see ... https://www.npmjs.com/package/promises-aplus-tests

    says

    This suite tests compliance of a promise implementation with the Promises/A+ specification.

    So I realise now that my package does NOT need to pass your tests and I am not as clever as I might have hoped :)

    However, I am running a test to ensure that the Promise library we are using passes your tests during the build process. If it does not the build will fail.

    My code is now ...

    class HTTPromise {
        constructor(options) {
            ... blah blah blah
            let executor = THE_REAL_EXECUTOR;
            return AppPromise(executor);
        } 
    }
    
    class HTTPromiseCompliance {
        constructor(executor) {
            return AppPromise(executor);
        }
    }
    

    AppPromise looks like ...

    module.exports = executor => {
        return new Promise(executor);
    }
    

    and finally the test suite ...

    describe('HTTPromises', function () {
    
        it('should use a Promises/A+ compliant Promise library', function (done) {
            this.timeout(0);
            var adapter = {
                deferred: function() {
                    var d = {}
                    var executor = function(resolve, reject) {
                        d.resolve = resolve
                        d.reject  = reject
                    };
                    d.promise = new HTTPromiseCompliance(executor);
                    return d
                }
            }
    
            promisesAplusTests(adapter, function (err) {
                if (err != null) {
                    console.log('promises Aplus Tests failed with ' + err.failures + ' failures');
                } else {
                    console.log('promises Aplus Tests passed');                
                }
                done(err);
            });
        });
    });
    

    A grunt task for this is fired during the build process.

    If anyone has any thoughts on 'my new thinking' I would be appreciative.

    Thanks