2

data below is being returned undefined:

function selectSearchedTicker(ticker, load) {
    var deferred = $q.defer();

    GetTickersFactory.getTickers('searched', load).then(function(data) {
        console.log('data',data);
        var tempTickers = GetTickersFactory.returnSearchedTickers();
        $rootScope.$emit("select.searched.ticker", tempTickers);
        deferred.resolve();
    });

    return deferred.promise;
}

This is the GetTickersFactory.getTickers function:

function getTickers(type, load, searchedTicker, cached) {
    type = type || 'portfolio';
    load = load || '';
    searchedTicker = searchedTicker || {};

    var deferred = $q.defer(),
        promise;

    if (cached) portfolioCached = cached;

    //other code remains same
    tickersPane = ScopeFactory.getScope('tickersPanel');

    switch (type) {
        case 'searched':
            return ApiFactory.getTickers(null, load).then(function(data) {

                //renderTickers just does logic, no more API call
                searchedTickers.that = renderTickers(data.data.tickers, searchedTicker, 'searched');
                promise = searchedTickers.that;
                console.log('promise',promise); // <-- there is data here
                deferred.resolve(promise);
                // return returnData(searchedTickers.that);
            });
            break; // <-- after hitting this break, chrome tools hits the Angular code
        //...
    }

    return deferred.promise; // <- also tried return promise

    // other functions...
}

You can see above promise = searchedTickers.that; when I log that, I see the data I want returned inside it. However it never makes it back to my original selectSearchedTicker function.


Update: Added this code which precedes everything, I had to make a promise chain, because I need to wait till TickersSelectFactory.selectSearchedTicker is called and the results of those actions are finished, before I call the 2 $emit's.

TickersSelectFactory.selectSearchedTicker(fullTicker.ticker, selectTickerUrl).then(function(res) {
    console.log('selectSearchedTicker promise returned');
    $rootScope.$emit("clear.tags.array");
    $rootScope.$emit("search.ticker.clicked", fullTicker);
});

Originally it looked like this and was giving me problems:

TickersSelectFactory.selectSearchedTicker(fullTicker.ticker, selectTickerUrl);
$rootScope.$emit("clear.tags.array");
$rootScope.$emit("search.ticker.clicked", fullTicker);
Leon Gaban
  • 36,509
  • 115
  • 332
  • 529
  • I'm not entirely sure why the OP is creating his own `$q.defer()` objects at all... – Alnitak Jan 15 '16 at 19:37
  • Well, actually at that point in my code I just `$emit` to send the Array to another Controller... I actually think I may have found my problem – Leon Gaban Jan 15 '16 at 19:38
  • @Alnitak added additional code so you see why I needed the Promise chain – Leon Gaban Jan 15 '16 at 19:41
  • my point was that you can usually just `return someOtherFuncThatReturnsAPromise()` from inside the `.then` callback and you get a chain for free – Alnitak Jan 15 '16 at 19:45
  • There is no need to wrap a promise around a promise for A+ promises, doing so is considered an [anti-pattern](http://stackoverflow.com/questions/23803743/what-is-the-explicit-promise-construction-antipattern-and-how-do-i-avoid-it) – MinusFour Jan 15 '16 at 19:46
  • @MinusFour yes, that's the point I was trying to make – Alnitak Jan 15 '16 at 19:47
  • Code hard to follow when `renderTickers()` isn't defined anyhwere – charlietfl Jan 15 '16 at 19:49
  • @MinusFour so you are saying you only need to use one Promise? What happens if there are multiple API calls, all which need to wait for data deeper in the code? – Leon Gaban Jan 15 '16 at 19:50
  • @LeonGaban rather than wrap one promise in another new one....just return the first promise. Keep in mind that `$http` returns a promise and you can do things like `return $http...` – charlietfl Jan 15 '16 at 19:52
  • @LeonGaban is `renderTickers` also asynchronous (and returning a promise) ? – Alnitak Jan 15 '16 at 19:53
  • It's part of the A+ resolution spec, `then` returns a new promise that will be fulfilled or rejected depending on the contents of the invoked handler. – MinusFour Jan 15 '16 at 19:56
  • No, renderTickers just does some frontend logic, sorry I should have mentioned that. See my answer below guys, this is why I couldn't just return the promise. No data returned! So I need to use another promise so insure data was returned. If there is a better more Angular way, please let me know... – Leon Gaban Jan 15 '16 at 19:56
  • It's not an "angular" thing, it's a "promise" thing. If you had just put `return searchedTickers.that` in the original code (and left the outer `return` in front of `ApiFactory.getTickers(...)` then you wouldn't have needed the `$q.defer()` object at all. – Alnitak Jan 15 '16 at 19:57

2 Answers2

3

The correct resolution to your problem is to use the built-in chaining that .then() provides, and do away with the extraneous $q.defer() object:

switch (type) {
    case 'searched':
        return ApiFactory.getTickers(null, load).then(function(data) {
            searchedTickers.that = renderTickers(data.data.tickers, searchedTicker, 'searched');
            return searchedTickers.that;
        });
    ...
}

similarly in your first function:

function selectSearchedTicker(ticker, load) {
    return GetTickersFactory.getTickers('searched', load).then(function(data) {
        console.log('data',data);
        var tempTickers = GetTickersFactory.returnSearchedTickers();
        $rootScope.$emit("select.searched.ticker", tempTickers);
        return tempTickers;
    });
}

(note the new return at the start of the function, that ensures that the caller of this function correctly receives a Promise)

Alnitak
  • 334,560
  • 70
  • 407
  • 495
  • Ah I see what you are saying, trying it out now... was kinda hard to grasp it with just comments hehe – Leon Gaban Jan 15 '16 at 20:04
  • 1
    The important bit is the _two_ `return` statements - the one _inside_ the `.then()` callback, and the one at the start that returns the result of the entire chain. They're all that's necessary to correctly chain promises. – Alnitak Jan 15 '16 at 20:05
  • Thanks! I'll avoid Promise within Promises now, also googling `A+ resolution spec` – Leon Gaban Jan 15 '16 at 20:08
  • All you really need to know is that the `return` value of a `.then` callback will always get converted into a `Promise` that is "resolved" with that value. i.e. if you just do `return f().then(function() { return 1 });` then the _final_ result of that chain is a promise that will (eventually) resolve to "1" (once `f()` has itself been resolved). If `f()` results in a "rejection", the whole chain will be rejected (and should be trapped with a `.catch()` call) – Alnitak Jan 15 '16 at 20:12
0

Figured it out, I was returning my ApiFactory call, which ended my promise chain, before I was able to add the data into the promise to resolve back upwards.

// return ApiFactory.getTickers(null, load).then(function(data) {
ApiFactory.getTickers(null, load).then(function(data) {

case 'searched':
    // With the return below, the code never got in to set the promise up and resolve it
    // return ApiFactory.getTickers(null, load).then(function(data) {
    ApiFactory.getTickers(null, load).then(function(data) {
        searchedTickers.that = renderTickers(data.data.tickers, searchedTicker, 'searched');
        promise = searchedTickers.that;
        console.log('searched promise',promise);
        deferred.resolve(promise);
    });
    break;
Leon Gaban
  • 36,509
  • 115
  • 332
  • 529
  • that will fix the problem, but your code still appears flawed. Is `renderTickers` an async function? – Alnitak Jan 15 '16 at 19:55
  • @Alnitak no, it just does some logic, not more API call. Sets some selected variable flags, makes some calls out to other Controllers / Services to change other variables. – Leon Gaban Jan 15 '16 at 19:56
  • Thanks, actually yeah looking at it now... I don't need to set it up that way... it should just be `promise = data.data.tickers` – Leon Gaban Jan 15 '16 at 19:59
  • 1
    Does `renderTickers()` just return its first parameter? Either way, you really should still get rid of that extra `$q.defer()` object. – Alnitak Jan 15 '16 at 20:04