JS, multiple JSON requests and callback functions

时间:2015-09-14 15:48:24

标签: javascript json api callback request

I need some help. I'm quite new to Javascript, I'm currently trying to do something using two differents api: SongKick and Deezer. The idea is simple, on the page you can type your city, 1) I do a first request to the Songkick Api to get the ID of this city, 2) then with the ID, I do another request to get a list of concerts and I only take the name of the artist (20 maximum), 3) then I with the list of names I use the deezer Api to get the picture of the artist and a mp3 preview.

I've tried many ways but I can't access the data everywhere of course, and I don't know how to use callback cause there is too many things, if you can take a look that would be awesome.

Thanks!

artistsArray = [];
artistsArray2 = [];
arr = [artistsArray,[],[]];
var dispName;

var areaId;

function search(){

area = document.getElementById('band').value;



function songKickArea(callback){
    $.getJSON('http://api.songkick.com/api/3.0/search/locations.json?query=' + area + '&apikey=tIhpFoFn0dWpQ72A',function(data){
    var areaId = data['resultsPage']['results']['location'][0].metroArea.id;

    callback(areaId);
    });
    console.log("1 is done");


}



function findAreaId(callback){
    songKickArea(function(callback){
        console.log(callback);
    });
    $.getJSON("http://api.songkick.com/api/3.0/metro_areas/" + areaId + "/calendar.json?apikey=tIhpFoFn0dWpQ72A",function(data){
            for (var i=0; i<20 ; i++)
            {
            artistsArray.push(data['resultsPage']['results']['event'][i].performance[0].displayName);
        }
        callback(artistsArray);
        });
    console.log("2 is done");

}




function addInfos(callback){
    for (var i=0; i<20 ; i++)
    {
        DZ.api('/search?q=artist:' + '"'+ artistsArray[i]+'"' +'?limit=1', function(json){
    if(json.data[0]){
        artistsArray2.push({preview:json.data[0].preview, picture: json.data[0].artist.picture})
    }   
    });
}
console.log("3   is done");
    callback();
}   

function runSearchInOrder(callback) {
    songKickArea(function() {
        findAreaId(function() {

                addInfos(function() {
            console.log(areaId);
        });

      });
    });
}
runSearchInOrder(function(){console.log('finished')});

}

EDIT 09/17/2015

Thanks Vittore, I took a look at promises in JS and it's very interesting and perfect in my case. So now I'm here :

function songKickArea(areaId){
    area = document.getElementById('band').value;
    return $.getJSON('http://api.songkick.com/api/3.0/search/locations.json?query=' + area + '&apikey=XXXXXXX',function(data){
    });

}
function findAreaId(data){
    var areaId = data['resultsPage']['results']['location'][0].metroArea.id;
    return $.getJSON("http://api.songkick.com/api/3.0/metro_areas/" + areaId + "/calendar.json?apikey=XXXXXXX",function(data){
    });
}

function addInfos(data){

    for (var i=0; i<20 ; i++)
    {
        artistsArray.push(data['resultsPage']['results']['event'][i].performance[0].displayName);
        DZ.api('/search?q=artist:' + '"'+ artistsArray[i]+'"' +'?limit=1', function(json){
            if(json.data[0]){
                artistsArray2.push({preview:json.data[0].preview, picture: json.data[0].artist.picture})
            }
        });
    }
}

And I use this onClick:

songKickArea().then(findAreaId).then(addInfos).then(createList);

So everything is working fine, in addInfos my array artistsArray2 get all the infos I need from deezer (preview and picture). But now the next step is to create a list to display these artists (or tracks) so the next function is like this.

function createList(json){

    var html = '<ul>';

    for (var i=0; i<17; i++)
    {
        html+= '<li>';
        html += '<div class="picture">' + '<div class="player"><img  src="svg/play43.svg" ></div>'+ '<a href=' + artistsArray2[i].preview + '>' + '<img src=' + artistsArray2[i].picture + '>' + '</a>' +'</div>';
        html+= '<div class="arrow"><img src="css/svg/arrow487.svg"></div>';
        html+= '</li>';
    }
    html+= '</ul>';
    $('#results').append(html);
}

But here I have no idea how to pass the value of a full array from the last function to this one, could you help me ? Thanks a lot !

1 个答案:

答案 0 :(得分:0)

更新:对服务和结果数组的多次调用几乎没有说明。

原始代码具有addInfos方法,该方法通过数组迭代(for循环)并在该循环中调用Web服务。你想要做的是将所有这些调用的结果放在一起。虽然有很多方法可以做到这一点,但我向您展示的是使用array.map将数组的每个元素“映射”到步骤X中的数据到AJAX调用返回的promise。让我举个例子:

假设你有一个数组:

var artistIds = [6664009,6664010,6664011]

现在你可以将它映射到promises:

var artistCalls = artistIds.map(function(id) {
     return $.getJson('~ get artists data service url ~' + id)
}

这将为您提供数组artistCalls,其中每个元素最终都会解析您需要的数据的承诺。虽然你可以用它做所有疯狂的事情,但从ALL调用中获取数据的最简单方法是使用$.when辅助方法:

$.when(artistCalls).then(function(artists) {
      // here artists will array where each element is data returned by each AJAX call
})

现在,如果你想渲染html以显示页面上的所有艺术家,你可能会有这样的代码:

function renderArtistHtml(artist) {
   return '<li>'
        += '<div class="picture"><div class="player"><img  src="svg/play43.svg" ></div><a href="' + artistsArray2[i].preview + '"><img src="' + artistsArray2[i].picture + '"></a></div>'
        += '<div class="arrow"><img src="css/svg/arrow487.svg"></div>'
        += '</div></li>';
}  

一个呈现整个列表的函数:

function renderAllArtistsHtml(artists) {
   return '<ul>' + artists.map(renderArtistHtml) + '</ul>'
}

现在您拥有了它,您可以一起创建整个功能链:

$(... my button selector ...).on('click',function(e) {

   var area = ... get area

   songKickArea(area)
        .then(findAreaId) // this thing returns promise that returns array
        .then(addInfos)   // this thing returns $.when(arr.map(...))
        .then(renderAllArtistsHtml) // this thing converts array of data from all calls from previous step to html layout
        .then(function(html) {  // this part just adds it to DOM
$('#results').append(html);
   });

})

刚刚回答了类似问题here

基本上jquery中的每个ajax方法都会返回promise(如果你的api没有返回promise(比如DZ.api),你可以将它包装在$.deferred

从功能中返回承诺后,您可以链接它们:

function myajax1() {
   return $.getJson(...)
}

function myajax2(data) {
   return $.getJson(...)
}


myajax1().then(myajax2)

这将使用myajax2 ajax call

返回的数据调用myajax1

您可以根据需要多次链接。

如果你需要等待几个,你可以使用$ .when:

$.when([myajax11(), myajax12()]).then(myajax2)

如此接近您的实际代码,您有3个api调用:

  • songkick locations
  • songkick metro_areas
  • DZ.api

最后一个需要在承诺中结束,请参见此处的示例:https://learn.jquery.com/code-organization/deferreds/examples/

声明3个函数:

function getLocations(area) {
   return $.getJson(....) // location query
}

function getMetroArea(data) {
   var areaId = data['resultsPage']['results']['location'][0].metroArea.id
   return $.getJson(...)  // metro query 
}

function getArtists(data) {
    var artist = data['resultsPage']['results']['event'][i].performance[0].displayName

    return DZAPIWraper(...)

}

并将它们链接起来:

getLocations(...).then(getMetroArea).then(getArtists)

如果你真的需要在最后一步为几位艺术家做几次调用,你的代码将类似于:

 function getArtists(data) {

    var artists = getArtistsArrayFromMetro(data)

    var artistsCallbacks = artists.map(function(a) {
         return DZAPIWrapper(...)
    })
    return $.when(artistCallbacks)
 }

并且完整链是:

getLocations(...).then(getMetroArea).then(getArtists).then(function(artists) {
  // here artists going to be all artists data from all api calls to DZ.api

})