Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

wrapAsync #548

Merged
merged 7 commits into from
Sep 28, 2016
Merged

wrapAsync #548

merged 7 commits into from
Sep 28, 2016

Conversation

lpww
Copy link
Contributor

@lpww lpww commented Sep 27, 2016

Add wrapAsync. A function that wraps a function that returns a promise, transforming it to a function which accepts the same arguments and returns a Highland Stream instead.

Addresses #517

@vqvu I didn't add any additional error handling because it looks like the constructor already handles promise errors in the same way as _.fromError (by creating an error stream with a single value). I've included that code for reference:

function promiseStream(StreamCtor, promise) {
    if (_.isFunction(promise['finally'])) { // eslint-disable-line dot-notation
        // Using finally handles also bluebird promise cancellation
        return new StreamCtor(function (push) {
            promise.then(function (value) {
                return push(null, value);
            },
                function (err) {
                    return push(err);
                })['finally'](function () { // eslint-disable-line dot-notation
                    return push(null, nil);
                });
        });
    }
    else {
        // Sticking to promise standard only
        return new StreamCtor(function (push) {
            promise.then(function (value) {
                push(null, value);
                return push(null, nil);
            },
            function (err) {
                push(err);
                return push(null, nil);
            });
        });
    }
}

Copy link
Collaborator

@vqvu vqvu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Trying out the new code review feature. See my comments.

addToplevelMethod('wrapAsync', function (f) {
var stream = this;
return function () {
var self = this;
Copy link
Collaborator

@vqvu vqvu Sep 27, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You don't need a self here. Just pass this to f.apply directly.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

var stream = this;
return function () {
var self = this;
var args = slice.call(arguments);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can save an allocation by passing arguments to f.apply directly.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool! I've always assumed that arguments must be converted to a proper array before you can use it like this.

return function () {
var self = this;
var args = slice.call(arguments);
return stream(f.apply(self, args));
Copy link
Collaborator

@vqvu vqvu Sep 27, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Technically f.apply() should not throw, but we should wrap it in a try-catch just in case.

Furthermore, stream can throw an error if the result of f.apply(...) is not an acceptable type. Since stream is less restrictive than we are, we should also check that the result is a promise before passing it to stream. If it is not a promise, we should return a _.fromError with an appropriate error.

We don't want the wrapped function to ever throw since it is supposed to be async.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I followed along on this part. Please take a look at my updates

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep. What you did was correct.

* @param {Function} f - the function that returns a promise
* @api public
*
* var Promise = require('bluebird');
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Promise.resolve and Promise.reject are standard functions, so I'd prefer not to mention a specific Promise library here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. Removed the bluebird reference.

@@ -7389,6 +7389,65 @@ exports['wrapCallback - default mapper discards all but first arg'] = function (
});
};

exports.wrapAsync = function (test) {
Copy link
Collaborator

@vqvu vqvu Sep 27, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The current tests are all over the place, but please use the nodeunit grouping feature:

exports.wrapAsync = {
    'basic functionality': function (test) {
        ...
    },
    'context': function (test) {
        ...
    },
    ...
};

This will group all of the wrapAsync tests together and allow us to manually run all only the tests in the group. It'll also be useful when I go in and finally split up this giant file.


Also, please add a test.expect(numAsserts) at the start of every test so that omitted assertions will fail the test.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup. I just copied the wrapCallback tests. It's good to know that you can group them like this and pass in the expected number of assertions.

};

exports['wrapAsync - context'] = function (test) {
var o = {
Copy link
Collaborator

@vqvu vqvu Sep 27, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this test verifies that the context is correctly passed. You need to use this inside f in some way. This is probably a copy-paste error since the wrapCallback version checks the context with test.equal(this, o);.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are correct. This is fixed now too.

@lpww
Copy link
Contributor Author

lpww commented Sep 27, 2016

Also, I'm aware that I have not updated the changelog. I have some questions about that and am planning to do it after the rest of the work is compete.

try {
promise = f.apply(this, arguments);
if (!_.isObject(promise) || !_.isFunction(promise.then)) {
throw (new Error('Wrapped function did not return a promise'));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From a code style standpoint, I don't like explicitly throwing exceptions from within a try-catch like this. It looks wrong at first glance. You should just return _.fromError(...) here.

@vqvu
Copy link
Collaborator

vqvu commented Sep 28, 2016

Just one minor point. Everything else looks good.

@lpww
Copy link
Contributor Author

lpww commented Sep 28, 2016

@vqvu I removed the throw from the try block and added an extra test for that specific error. I'm ready to update the changelog but I'm not sure what version to put this under. Would it be a new version 2.11.0?

@vqvu
Copy link
Collaborator

vqvu commented Sep 28, 2016

It should be under 3.0.0.

@vqvu vqvu merged commit 7957db2 into caolan:master Sep 28, 2016
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants