0

I'm having a hard time understanding how promises works. I've seen some examples and some of it makes sense. However, when I try to actually use it I get stuck. I have the following example: (I'm using q and nodejs. getItem() returns a promise, but my function doesn't wait for it.)

function calculate(id, item) {
   var calcId = id ? id : getItem(item);
   if (!id && calcId) { calcId = calcId.Id; }

   if (calcId) {
      update(calcId);
   } else {
      insert(item);
   }
}

Based on the examples, I don't see how to do it without code duplication.

  • What do you mean, "doesn't wait for it"? How else would it access its `.Id`? Or is that a property of the promise? – Bergi May 21 '14 at 20:21

3 Answers3

2

Promises are resolved asynchronously, so you can't treat them as synchronous as in your example. What you want to do is coerce the value is a Q promise if needed, then proceed from there, that way you have deterministic behavior and no code duplication.

I'm making some assumptions here about update and insert returning promises and returning them when needed so that calculate itself returns a promise.

function calculate( id, item ) {
    return Q( id || getItem(item) ).then(function( calcId ) {

        // Code seems weird to be, but it's based on the original example.
        // Might want to review this.
        if ( !id && calcId ) {
            calcId = calcId.Id;
        }

        return calcId ?
            update( calcId ) :
            insert( item );
    });
}
dherman
  • 2,832
  • 20
  • 23
1

Don’t duplicate your id checks:

function calculate(id, item) {
    var calcId;

    if (id) {
        calcId = id;
    } else {
        calcId = getItem(item).Id;
    }

    if (calcId) {
        update(calcId);
    } else {
        insert(item);
    }
}

Now make calcId consistently a promise holding an Id:

function calculate(id, item) {
    var p;

    if (id) {
        p = Promise.resolve({ Id: id });
    } else {
        p = getItem(item);
    }

    return p.then(function (calcId) {
        if (calcId.Id) {
            return update(calcId.Id);
        } else {
            return insert(item);
        }
    });
}

Where, in the case of Q, Promise.resolve is Q.resolve, or just Q.

Bonus: as a generator!

function calculate(id, item) {
   var calcId = id ? id : yield getItem(item);
   if (!id && calcId) { calcId = calcId.Id; }

   if (calcId) {
      update(calcId);
   } else {
      insert(item);
   }
}
Ry-
  • 218,210
  • 55
  • 464
  • 476
  • Since id is undefined and I need to retrieve the item (getItem()) how exactly is q.resolve helping? That code is never reached. getItem() is the one I have to wait for... –  May 21 '14 at 20:07
  • @user3582665: Sorry, I completely forgot the `then`. – Ry- May 21 '14 at 20:09
0

Several points :

  • Promise-wrapped results need to be handled with a function specified as a parameter of a promise method (eg .then(fn))
  • Q(x) can be used to ensure that x is Promise-wrapped. The operation is transparent if x is already a promise - it won't be double wrapped
  • you need safety in case both id and item are empty or missing
  • you need further safety in case calcId is falsy and item was not provided
  • id versus calcId.Id can be more elegantly catered for.
function calculate(id, item) {
    if(!id && !item) {
        throw new Error("'calculate(): parameters empty or missing'");
    }
    return Q(id ? {Id:id} : getItem(item)).then(function(resultObj) {
        var calcId = resultObj.Id;
        if(calcId) {
            update(calcId);
        } else {
            if(item) {
                insert(item);
            } else {
                throw new Error("'calculate(): `calcId` is falsy and `item` is empty of missing'");
            }
        }
        return calcId; //in case you need to chain calculate(...).then(...)
        //(Alternatively, if update() and insert() return promises, then return them instead as per dherman's answer)
    });
}

Call as follows :

calculate(myId, myItem).then(function(calcId) {
    //(optional) anything else you wish to do with `calcId`
}).catch(function(e) {
    console.error(e);
});
Roamer-1888
  • 19,138
  • 5
  • 33
  • 44