-1

I'm trying to call a function once after all ajax calls are done. The $.when below is being called too soon with an empty array of promises. searchRecommendations() is being called several times from the success of a few previous $.ajax calls. Could that be responsible somehow?

var MAX = 2; //Maximum levels queried
var ARTISTS = [];  //Multidimensional array with the number of potential 'artists' i.e. compare Madonna, to Beethoven to Eminem to nth-artist
var RELEVENT_ARTISTS = 2; //Number of relevent artists added to the list for each new artist 
var promises = []; 

 $(function(){
    init(0);
 })


   function init(i){
   for(var i; i<1; i++){

        console.log('searchArtists for relevant artist '+$('input')[i].value)   
        var artist = $('input')[i].value;
        $.ajax({
            url: 'https://api.spotify.com/v1/search',
            data: {
                q: artist,
                type: 'artist'
            },
                success: function (response) {
                    console.log(response.artists.href);
                    searchRecommendations(response.artists.items[0].id, 0)  
                    //nextLevel(0)
            }
        });


    }
    //console.log(ARTISTS)  
    //getMatches(ARTISTS)   
}



function searchRecommendations(artist, depth) {
            console.log(' ')                
            console.log('searchRecommendations '+artist+ ' '+ depth )   
            if(depth == MAX){ return console.log('max reached '+depth) } else {
                    promises.push(
                        $.ajax({
                            url: 'https://api.spotify.com/v1/artists/' + artist + '/related-artists',
                            data: {
                                type: 'artist',
                            },
                            success: function (response) {

                                console.log('RESPONSE');
                                console.log(response)
                                for(var r=0; r<RELEVENT_ARTISTS; r++){
                                    console.log(response.artists[r].name)
                                    var obj = { 'artist' : response.artists[r].name,  'level':(depth+1)*5 } 

                                    ARTISTS.push(obj)

                                    searchRecommendations(response.artists[r].id, depth+1)  //Recursion
                                }
                            }       
                        })
                    )
            }   
}





$.when.apply(undefined, promises).done(function() {
    convert_artists_to_nodes_and_links(ARTISTS)
    console.log( 'this is being called too soon')
    console.log( promises )
})

How to wait until jQuery ajax request finishes in a loop?

Community
  • 1
  • 1
Squirrl
  • 4,909
  • 9
  • 47
  • 85
  • Where is `searchRecommendations` called? Where are `MAX` and `ARTISTS` defined? Why do you try to `return` `setTimeout` from `searchRecommendations`? – guest271314 Feb 01 '17 at 02:57
  • You can try `.ajaxStop()` it waits for all the Ajax calls to complete. Here is the documantation: http://api.jquery.com/ajaxStop/ – Girisha Feb 01 '17 at 02:59
  • 3
    the code you have will wait in an empty `promises` array ... if the function is called later by some means, the `$.when` doesn't "re-run" magically ... promises are not a substitute for event driven code – Jaromanda X Feb 01 '17 at 03:00
  • @guest271314 even without setTimeout I have this issue. – Squirrl Feb 01 '17 at 03:01
  • now that you've added the code for function `init` .. once again .. where and when does `init` get called - same problem, different level – Jaromanda X Feb 01 '17 at 03:01
  • Init gets cAlled on page load $(function()... – Squirrl Feb 01 '17 at 03:04
  • @Squirrl Substitute `$.when()`, `.apply()`, `$.map()` for `for` loops. – guest271314 Feb 01 '17 at 03:04
  • 1
    OK ... so why do you think that `$.when` will wait for `init` to finish before executing? ... your code is like having `var promises = [];` followed **immediately** by `$.when...` and then your function definitions ... as I said earlier, `$.when` doesn't check to see when promises has data in it ... how would it know when it's all been added? – Jaromanda X Feb 01 '17 at 03:06
  • What is purpose of `for` loops within both `init` and `searchRecommendations`? – guest271314 Feb 01 '17 at 03:09
  • so what you want to happen is ... 1. `init` is called on page load, 2. `init` does it's thing, calling `searchRecommendations` which creates an array of promises, and then, 3. `$.when` waits for the promises to complete and does it's thing ... right? – Jaromanda X Feb 01 '17 at 03:19
  • @JaromandaX Yes that's right. – Squirrl Feb 01 '17 at 03:21
  • @guest271314 One for loop is to make many ajax calls, and the other for loop cycles through the returned response/s. – Squirrl Feb 01 '17 at 03:24
  • What is expected result of iterating `ARTISTS`at `init` then pushing `obj` to `ARTISTS` array at `searchRecommendations`? – guest271314 Feb 01 '17 at 03:26
  • One huge problem is that `searchRecommendations` is pushing to `ARTISTS`, which is used for the loop in `init` - but the `ARTISTS` array is used to index into the "array" of `` in the page - so ... that's just ugly – Jaromanda X Feb 01 '17 at 03:27
  • `RELEVENT_ARTISTS` ... what is that? how is the index (0...n) relevant to the index of `response.artists` ?? – Jaromanda X Feb 01 '17 at 03:30
  • I agree, that was an ugly oversight, but I still have the same problem when I just call it once. – Squirrl Feb 01 '17 at 03:30
  • Have you completed the process without `for` loop? When you call what "once"? What is `ARTISTS`? – guest271314 Feb 01 '17 at 03:32

1 Answers1

1

Ok, big refactoring of your code, but I think this will do what you want - sorry I can't test it - I'll attempt to add comments slowly to explain the code

function init(i) {
    // $('input.artist') - as I can't see your HTML, I would recommend adding cclass='artist' to inputs that you want to process
    // [].map.call($('input.artist'), function(artist, i) { - iterate through the inputs, calling the function which returns a promise
    // artistP will be an array of Promises
    var artistsP = [].map.call($('input.artist'), function(artist, i) {
        console.log('searchArtists for relevant artist ' + $('input')[i].value)
        var artist = $('input')[i].value;
        // return the promise returned by $.ajax
        return $.ajax({
            url: 'https://api.spotify.com/v1/search',
            data: {
                q: artist,
                type: 'artist'
            }
        })
        // the returned promise needs to wait for the promise returned by searchRecommendations
        .then(function(response) {
            console.log(response.artists.href);
            return searchRecommendations(response.artists.items[0].id, 0);
        });
    });
    // wait for the promises to complete and we're done
    $.when.apply($, artistsP).done(function() {
        convert_artists_to_nodes_and_links(ARTISTS)
    });
}

function searchRecommendations(artist, depth) {
    console.log('searchRecommendations ' + artist + ' ' + depth)
    if (depth == MAX) {
        // just return undefined to stop the recursion
        return console.log('max reached ' + depth);
    } else {
        // return a promise
        return $.ajax({
            url: 'https://api.spotify.com/v1/artists/' + artist + '/related-artists',
            data: {
                type: 'artist',
            }
        })
        // the promise needs to wait for the recursed artists
        .then(function(response) {
            // create an array of promises to wait on
            var promises = response.artists.map(function(artist) {
                console.log(artist.name)
                var obj = {
                    'artist': artist.name,
                    'level': (depth + 1) * 5
                }
                ARTISTS.push(obj)
                return searchRecommendations(response.artists[r].id, depth + 1) //Recursion
            });
            // return a promise that waits for all the "sub" requests
            return $.when.apply($, promises);
        });
    }
}
Jaromanda X
  • 53,868
  • 5
  • 73
  • 87