【问题标题】:JS, multiple JSON requests and callback functionsJS、多个JSON请求和回调函数
【发布时间】:2015-09-14 15:48:24
【问题描述】:

我需要一些帮助。我对 Javascript 很陌生,我目前正在尝试使用 两个不同的 api 做一些事情:SongKick 和 Deezer。这个想法很简单,在页面上你可以输入你的城市,1)我首先向 Songkick Api 请求获取这个城市的 ID,2) 然后使用 ID,我再次请求获取音乐会列表,我只获取艺术家的姓名(最多 20 个),3) 然后我使用 deezer Api 的姓名列表获取艺术家的照片和 mp3 预览。

我尝试了很多方法,但我当然不能到处访问数据,而且我不知道如何使用回调,因为东西太多了,如果你能看看会很棒。

谢谢!

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')});

}

编辑 09/17/2015

感谢 Vittore,我查看了 JS 中的 Promise,它对我来说非常有趣和完美。所以现在我在这里:

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})
            }
        });
    }
}

我使用这个 onClick:

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

所以一切正常,在 addInfos 中,我的数组 ArtistArray2 从 deezer 中获取了我需要的所有信息(预览和图片)。但是现在下一步是创建一个列表来显示这些艺术家(或曲目),所以接下来的功能是这样的。

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);
}

但是在这里我不知道如何将完整数组的值从最后一个函数传递给这个函数,你能帮帮我吗?非常感谢 !

【问题讨论】:

    标签: javascript json api callback request


    【解决方案1】:

    更新:对多次调用服务和结果数组的说明很少。

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

    假设你有一个数组:

    var artistIds = [6664009,6664010,6664011]
    

    现在您可以将其映射到 Promise:

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

    这将为您提供数组artistCalls,其中的每个元素最终都会使用您需要的数据解决promise。虽然你可以用它做所有疯狂的事情,但从所有调用中获取数据的最简单方法是使用 $.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 并使用myajax1 ajax 调用返回的数据

    您可以将其链接多次。

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

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

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

    • songkick 地点
    • 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
    
    })
    

    【讨论】:

    • 非常感谢!我在编辑帖子时遇到了一些新问题,因为我不知道如何将一个完整的数组从一个函数传递给另一个函数,你能看看吗?谢谢
    • @MaximeBéneteau 我已经在我的回答中回答了你的问题——看看getArtistssn-p。您正在创建一系列承诺并返回 $.when(arrayOfPromises)。最后一个片段是您的完整点击处理程序主体 - 在最后一个处理程序中您将呈现您的 html。
    • 我知道,我试过了,但它对我来说仍然很复杂(我是一名开始编码的设计师)我不太了解可变艺术家,或者在哪里做我的循环。我理解这个想法,但找不到如何将其应用于我的代码
    • 非常感谢你,这是完美的!最后一件事,当我向 Deezer 查询 deezer 上不存在的艺术家时,如何避免出现未定义的错误?
    • @MaximeBéneteau 欢呼。您需要将 deezer api 的调用包装在一个承诺中(我给了您一个带有示例的链接)并且需要在那里处理错误,并返回类似空对象的东西。然后在最后一步,不是将整个 artists 数组映射到 html ,而是先将其过滤 (google mdn array.filter ) 到有数据的数组,然后再映射。
    猜你喜欢
    • 2012-07-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-03-11
    • 1970-01-01
    相关资源
    最近更新 更多