0

I have an object whose value comes from an AJAX request. I'm converting it to a promise and am encountering some behaviour with promises that I don't expect. I have an example here that exhibits the same behaviour, but I'm substituting Q.all() for my AJAX request.

(Thing = function(){
  var promise;
  var refresh = function() {
    promise = Q.all(["a", "b"]);
  };

  var fetch = function(i) {
    return promise.then(function(promiseVal){
      return promiseVal[i];
    });
  };

  refresh();

  return {
    "fetch": fetch,
    "refresh": refresh,
    "promise": promise
  };

}());

Thing is executed on load and runs "refresh" to populate itself initially. The point of the "fetch" function is that my async request (Q.all in this case) returns a promise for an array, but what I really want is a promise for elements in the array (e.g. a promise for "a" or a promise for "b"). So I'm expecting Thing.fetch(1) to return a promise for "b".

Thing.fetch(1) does return a promise, but if I do Thing.fetch(1).valueOf() it returns a promise, not "b" as I was expecting. If I do:

Thing.fetch(1).then(function(foo){
  console.log(foo);
});

it will print "b" on the console.

If I do Thing.promise.valueOf() it returns the array, so the promise is resolved when I call "fetch".

So my question is why doesn't valueOf() return a value when I call it on the promise returned by "fetch"?

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
Gregory Bell
  • 1,861
  • 1
  • 19
  • 21
  • 1
    Notice that by calling `Thing.refresh()` you do not alter the value of `Thing.promise`. – Bergi Jul 18 '13 at 21:47

2 Answers2

0

It seems your promise is not resolved yet.

It's not well documented, but scattered over the wiki pages I found:

The valueOf call returns the promise itself by default.

The valueOf method is useful for providing information about the promise in the same turn of the event loop. For example, resolved promises return their resolution value and rejections return an object that is recognized by isRejected.

If the promise is fulfilled, promise.valueOf() returns the fulfilled value. If the promise is or has forwarded to a deferred promise, it returns most recently deferred promise. For rejected promises, promise.valueOf() returns a sentinel object with {rejectedPromise: true, reason: {}}

When you're doing Thing.fetch(1).then(console.log); it will of course print "b" on the console - that's the resolve value which is passed to the callback. But notice that it will invoke the log function in the future!

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • As far as I can tell the promise should be resolved. If I go `Thing.promise.valueOf()` it's resolved and I get ["a", "b"]. If I call `Thing.fetch(1).valueOf()` after that it returns a promise. I'm doing this by hand so it's not like I'm calling it too fast. If the promise returned by fetch() isn't resolved I'd like to know why before I can accept the answer. – Gregory Bell Jul 18 '13 at 22:31
  • `promise` is created as an already-resolved Promise. Yet, `then` [is guaranteed](http://promises-aplus.github.io/promises-spec/) to be asynchronous, so `Thing.fetch()` will yield a Promise that gets resolved in the future. See also [the Q tutorial](https://github.com/kriskowal/q#tutorial): "*resolution of a promise is always asynchronous*" – Bergi Jul 18 '13 at 22:44
  • Ok. After some experimenting I understand what you're talking about. `Thing.fetch(1).valueOf()` all happens in the same turn of the event loop, so even though `promise` has resolved at this point the new promise from `fetch()` has NOT resolved, so `valueOf()` shows me the promise. If I go `var a = Thing.fetch(1); a.valueOf();` I get the value for the promise because the event loop has ticked and the new promise from `fetch()` has resolved. I'm going to clean up your answer a bit to make this explicit and then accept it. Thanks! – Gregory Bell Jul 18 '13 at 22:59
  • I was doing some more experimenting and I figured out that `var a = Thing.fetch(1); a.valueOf();` doesn't work. If I have it as two lines in the console it works, but if I add those lines in my file `a.valueOf()` remains a promise. So when will the promise returned by fetch be resolved? – Gregory Bell Jul 18 '13 at 23:33
  • In [Q's `nextTick`](https://github.com/kriskowal/q/wiki/API-Reference#qnexttickcallback) - quite fast but after the current execution. If you execute them together it will be that same as `Thing.fetch(1).valueOf()` (and the next tick is after that execution), but when you use the console and submit it in two pieces, the tick will happen in between. – Bergi Jul 19 '13 at 00:36
0

Because the value of the promise is an array. The value of the function passed to "then" will be some value. Your fetch method is not really returning a promise, it is resolving a promise.

aet
  • 7,192
  • 3
  • 27
  • 25