1

This is basically my code, using q:

let d = Q.defer();
let result = {
    name: 'peter'
};

d.resolve(result);
return d.promise;

However, I now need to perform a step based on a certain condition. This step is calling another object that also returns a promise. So I'm having nested promises, if that's the correct term.

Something like this:

let d = Q.defer();
let result = {
    name: 'peter'
};

if (someParameter) {
    otherService.getValue() // Let's say it returns 'mary'
        .then((res) => {
            result.name = res;
        });
}

d.resolve(result);
return d.promise;

This doesn't work however (the name property is still 'peter'). Probably due to the fact that my inner promise is resolved at a later moment?

I've also tried this, but it doesn't work if I call the otherService which returns a promise. It does work if I just set the value:

let d = Q.defer();
let result = {
    name: 'peter'
};

d.resolve(result);
return d.promise
    .then((data) => {
        if (someParameter) {
            // Works
            data.name = 'john';

            // Doesn't work
            otherService.getValue()
                .then((res) => {
                    data.name = res;
                });
        }

        return data;
    });

Here the name will be 'john', not 'mary'.

Clearly I'm misunderstanding promises, but I can't wrap my head around it.

Peter
  • 13,733
  • 11
  • 75
  • 122

2 Answers2

2

Using deferreds is deprecated. If at all, you should use the Q.Promise constructor, but you don't even need that. Just use the Q function to create a promise that is fulfilled with your value.

Now you can (and should) simplify your code to

let d = someParameter ? otherService.getValue() : Q('peter');
return d.then(res => ({
    name: res
}));
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
1

Control flow with Promises is...fun?

Anyway, you are nearly there, you can embed a Promise in a Promise as well as chain them. However, if you are embedding them, you must return the embedded Promise chain:

let d = Q.defer();
let result = {
    name: 'peter'
};

d.resolve(result);
return d.promise
    .then((data) => {
        if (someParameter) {

            // Should work now that we return the Promise
            return otherService.getValue()
                .then((res) => {
                    data.name = res;
                    // And we have to return the data here as well
                    return data;
                });
        }

        return data;
    });

Promise resolve can take a value or another Promise and it will handle the flow. So, when we return inside a then, the value we are returning can be another Promise or just a value. The machinery will take care of unwrapping for us.

Davin Tryon
  • 66,517
  • 15
  • 143
  • 132
  • Your code is working, but so overcomplicated that I have to deem it not useful. – Bergi Sep 21 '16 at 16:22
  • @Bergi sorry you feel that way. I just copied the OP in order to not confuse the author further. the code could be much more expressive, yes. Since the post was about Promise control flow, it is necessary to show a full example. It seemed to me that the author was confused about embedded promises etc. – Davin Tryon Sep 21 '16 at 16:39
  • It is complicated, but meanwhile I've refactored it. Also, my example here is trivial compared to what we have in our real code. But this answer explained to me what I was doing wrong. – Peter Sep 22 '16 at 08:15