0

I have Node.js application. Why these two code fragments do not work in the same way?

1)

function myFn1() {
    return new Promise(function(resolve, reject) {
        myFn2().then(function(url) {
            resolve(url);
        });
    });
}

2)

function myFn1() {
    return new Promise(function(resolve, reject) {
        myFn2().then(resolve);
    });
}

Common:

function myFn2() {
    return new Promise(function(resolve, reject) {
        var url = 'example.com';
        resolve(url);
    });
}

I call it with

myFn1().then(function(url) {
    console.log(url);
});

With code 1) url is passed properly, but with code 2) url is undefined. Do I not pass one-argument function in both cases? What is the difference?

Vikramaditya
  • 5,444
  • 6
  • 34
  • 45
WGawel
  • 72
  • 6
  • 3
    If I fix the typo (the `()` after `function` in `function() myFn2`), both work for me (as they should): https://jsfiddle.net/rorgqrjj/ I used "code 1" as `myFn1A` and "code 2" as `myFn1B`. – T.J. Crowder Dec 12 '16 at 11:58
  • ...but of course, you wouldn't want `myFn1` to work that way in any case. It's creating a new promise unnecessarily. You'd want `function myFn1() { return myFn2().then(function(url) { /* do something, presumably, and then: */ return url;}); }` or similar. – T.J. Crowder Dec 12 '16 at 11:59
  • `myFn2().then(resolve);` Shouldn't it be `myFn2().then(url => resolve(url));` What do you mean with one-argument function? – Ashwani Agarwal Dec 12 '16 at 12:40
  • @AshwaniAgarwal: Your two calls there do **exactly** the same thing (just with an extra unnecessary function in the middle) provided `then` calls its callback with one argument (which it does). – T.J. Crowder Dec 12 '16 at 13:27
  • 2
    Note http://stackoverflow.com/questions/23803743/what-is-the-explicit-promise-construction-antipattern-and-how-do-i-avoid-it – Benjamin Gruenbaum Dec 12 '16 at 13:36
  • Yes, my bad, I didn't understand the question exactly. – Ashwani Agarwal Dec 12 '16 at 14:22

1 Answers1

2

First off, avoid the promise anti-pattern. When you already have a promise, just use it and return it. Don't wrap it in a new promise. Besides being inefficient, it's real easy to make mistakes, particularly with error handling (which you have).

So, what you should be doing is this:

function myFn1() {
    return myFn2().then(function(url) {
        // do some modification to url and then return that
        // or some other processing here
        return url;
    });
}

There is simply no need to wrap the returned promise from myFn2() in another promise. In your specific case when doing so, you silently ate any errors from that inner promise and they were not propagated to the caller of myFn1() which is nearly always a bug, sometimes a very serious bug.

And, if you really have nothing to do inside the .then() handler, then you don't even need it either:

function myFn1() {
    // some code here
    return myFn2();
}

As to the original question, your two code fragments 1) and 2) work the same way and have no meaningful behavior differences other than one makes an extra function call and uses a little more stack to do so.

When you do this:

myFn2().then(resolve);

You're telling .then() to call resolve with whatever argument would normally be passed to the .then() handler. Since that argument in your case is url, then:

.then(resolve);

is exactly the same as:

.then(function(url) {
    resolve(url);
});

They both call resolve with exactly the same argument. The first is a shorthand and should be used when the argument that .then() will use for it's callback is exactly the argument you want to your function. The second should be used if the arguments for your function are not exactly the same or, obviously, if you have more than a single function call to do in the .then() handler. So, if you wanted to add a ".jpg" extension to the URL before calling resolve, then you'd have to do:

.then(function(url) {
    resolve(url + ".jpg");
});

With code 1) url is passed properly, but with code 2) url is undefined. Do I not pass one-argument function in both cases? What is the difference?

Assuming the promise returned by myFn2() resolves to url, then there should be no difference between your scenario #1 and #2. The promise returned by myFn1() should resolve to the same value either way.

Now, if you put some code in a .then() handler and forget to return the desired value from that .then() handler, then the resolved value of the promise becomes undefined which may be what is happening in your real code. So, if you do this:

function myFn1() {
    return myFn2().then(function(url) {
        console.log(url);
    });
}

myFn1().then(function(url) {
    console.log(url);     // will be undefined
})

Because you didn't return anything from the .then() handler inside of myFn1. That means the return value is undefined so the promise took on that return value. Just remember, if you want your promise to have a specific resolved value, you have to return it from any .then() handlers in the chain.

function myFn1() {
    return myFn2().then(function(url) {
        console.log(url);
        return url;            // make sure the promise retains this resolved value
    });
}
jfriend00
  • 683,504
  • 96
  • 985
  • 979