0

I have this code in a factory:

  getAyahsByJuz: function (juzIndex) {
        var response = [];
        var promises = [];

        var self = this;
        var deferred = $q.defer();
        $timeout(function () {
            $http.get('data/quran.json').success(function (data) {
                var ayahs = Quran.ayah.listFromJuz(juzIndex);
                angular.forEach(ayahs, function (value, key) {
                    var promise = self.getVerse(value.surah, value.ayah).then(function (res) {
                        var verse = {
                            surah: value.surah,
                            ayah: value.ayah,
                            text: res
                        };
                        response.push(verse);
                    }, function (err) {
                        console.log(err);
                    });
                    promises.push(promise);
                });
            });
        }, 30);

        $q.all(promises).then(function() {
            deferred.resolve(response);
        });

        return deferred.promise;
    },

Please note that everything is working fine the verse object is returning properly. However, when I use this in a controller using .then(res). res returns [] instead of the array filled with the verse objects.

Can anyone point out why? Thanks!

2
  • Did you try to put $q.all(promises) inside $timeout function? Commented May 25, 2015 at 0:45
  • @cespon yup, same result -> [] Commented May 25, 2015 at 0:47

1 Answer 1

3

The short answer is because your $q.all runs before $timeout & before the $http embedded in $timeout. Let's boil your original code down to its relevant components:

getAyahsByJuz: function (juzIndex) {
    var response = [];
    var promises = [];
    var deferred = $q.defer();
    // ...irrelevant stuff that will happen after a $timeout
    // this happens IMMEDIATELY (before $timeout):
    $q.all(promises).then(function() { // wait for empty promise array
        deferred.resolve(response); // resolve with empty response array
    }); // side note: this is a broken chain! deferred.promise can't reject

    return deferred.promise; // send promise for empty array
}

See the problem? If for some odd reason you need to keep that $timeout, here's the fix with substantial promise refactoring & removing the awful jquery-inspired non-promisy success syntax):

getAyahsByJuz: function (juzIndex) {
    var self = this;
    // $timeout itself returns a promise which we can post-process using its callback return value
    return $timeout(function () {
        // returning the $http promise modifies the $timeout promise
        return $http.get('data/quran.json').then(function (response) { // you never used this response!

            var versePromises = [];

            var ayahs = Quran.ayah.listFromJuz(juzIndex);
            angular.forEach(ayahs, function (value, key) {
                // we'll push all versePromises into an array…
                var versePromise = self.getVerse(value.surah, value.ayah).then(function (res) {
                    // the return value of this `then` modifies `versePromise`
                    return {
                        surah: value.surah,
                        ayah: value.ayah,
                        text: res
                    };
                });
                versePromises.push(versePromise);
            });

            return $q.all(versePromises); // modifies $http promise — this is our ultimate promised value
            // if a versePromise fails, $q.all will fail; add a `catch` when using getAyahsByJuz!
        });
    }, 30);

}

However, there is still a huge issue here… why aren't you using the server response of your $http call anywhere? What is the point of that first call?

Also I find that $timeout to be extremely suspicious. If you need it then it's likely there's something bad going on elsewhere in the code.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.