简体   繁体   中英

How to make promise/callback on forEach loop with async code

I think that I'm looking for promise but I don't see how to use it in my case.
I would like to console.log('ended') only when all my async code is done.
I have a callback for the DZ.api but not for forEach() , what is the best way for doing that ?

    _.forEach(filteredTracks, function(n) {
        DZ.api('/playlist/'+ n.id +'/tracks', function(response) {
            _.forEach(response.data, function(n) {
                SP.api('/search/'+n.title, function(response) {
                    console.log(response);
                });
            });
        });
    });
    console.log('ended');

You're looking for Promise.all -> Promise.all(arrayOfPromise).then(function (result) {})

With your current code, if SP.api is already Promise compatible, just do :

_.map(filteredTracks, function(n) {
    return DZ.api('/playlist/'+ n.id +'/tracks')
            .then(function (response) {
                _.map(response.data, function(n) {
                    return SP.api('/search/'+n.title);
                });

                return Promises.all(response.data);
            });
    });
});

Promise.all(filteredTracks)
    .then(function (results) {
        console.log('ended');
    });

If SP.api and DZ Api does not return a Promise you can :

Promisify SP.api DZ Api and and use the same code I wrote before

Or return promises in the ._map

_.map(filteredTracks, function(n) {
    var promise = new Promise(function (resolve, reject) {
        DZ.api('/playlist/'+ n.id +'/tracks', function (response) {
                _.map(response.data, function(n) {
                    var insidePromise = new Promise(function (resolve, reject) {
                        return SP.api('/search/'+n.title, function (result) {
                            resolve(result);
                        });
                    })

                    return insidePromise;
                });

                resolve(Promises.all(response.data));
            });
        });
    });

    return promise;
});

Promise.all(filteredTracks).then(function (results) {
        console.log('ended');
    });

What you want is to replace forEach with map and have each call return a Promise, concatenating the results of the inner loop since you have two levels. Then you pass the array of Promises to Promise.all() , which creates a Promise that resolves when all the Promises in the array have resolved. To do that, you'll have to refactor your code to return a Promises from that DZ.api function instead of using a callback. In short:

let promises = filteredTracks.map(track => {
    return DZ.promisedApi(track).then(response => {
        return Promise.all(response.data.map(n => SP.promisedApi(n))
    }) 
})

Promise.all(promises).then(() => console.log("done!"))

You did not specify what libraries you are using but overall I can recommend you to save the promises of all API calls into an array and then using Promises.all()

So you would have something like (WARNING: untested code, just for reference):

var promisesArray = []

_.forEach(filteredTracks, function(n) {
    DZ.api('/playlist/'+ n.id +'/tracks', function(response) {
        _.forEach(response.data, function(n) {
            promisesArray.push( SP.api('/search/'+n.title, function(response) {
                console.log(response);
            }));
        });
    });
});

Promise.all(promisesArray)
        .then(function(value){
          console.log('ended');
        })

Basically, as stated on the Promise reference page , Promise.all creates a new Promise that resolves when all promises passed as a parameter are resolved. Which in my opinion matches your use case.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM