2016-10-01 89 views
1

目前我正在試圖從與承諾Spotify的API獲取數據,昨天我得到了另外一個問題很大的幫助,就同一主題:「Loop the object returned from node promise and feed to the next .then」。從節點返回數據有望

我所做的是首先從我的播放列表中獲取曲目,然後調用另一個獲取藝術家的api。最後,我打電話給另一個獲取藝術家圖像的api。 現在我的問題是:我如何返回從我的承諾中獲得的數據?

這是我函數獲取播放列表網址:

function getPlaylists(access_token) { 

    var options = { 
     url: 'https://api.spotify.com/v1/me/playlists', 
     headers: { 'Authorization': 'Bearer ' + access_token }, 
     json: true 
    }; 

    return new Promise(function(resolve, reject) { 

     request.get(options, function(error, response, body) { 
     var playlists = body.items; 
     var playlistArray = []; 
     playlists.forEach(function(playlist) { 
      var name = playlist.name; 
      var url = playlist.tracks.href; 

      playlistArray.push(url); 
     }); 

     if(!error) { 
      resolve(playlistArray); 
     } else { 
      reject(error); 
     } 

    }); 

    }); 
} 

這一次得到的藝術家:

function getArtists(url,access_token) { 

    var params = { 
    url: url, 
    headers: { 'Authorization': 'Bearer ' + access_token }, 
    json: true 
    }; 

    return new Promise(function(resolve, reject) { 

    request.get(params, function(error, response, body) { 

     var tracks = body.items; 
     var artistArray = []; 
     tracks.forEach(function(artists) { 
      let allArtists = artists.track.artists; 
      allArtists.forEach(function(artist) { 
       artistArray.push(artist); 
      }); 
     }) 

     if(!error) { 
      resolve(artistArray); 
     } else { 
      reject(error); 
     } 

    }); 

    }) 

} 

而這一次得到的藝術家形象:

function getArtistImages(artistId) { 
    var options = { 
     url: 'https://api.spotify.com/v1/artists/' + artistId, 
     json: true 
    }; 

    return new Promise(function(resolve, reject) { 
     request.get(options, function(error, response, body) { 
      if(error != null) { 
       reject(error); 
      } else { 
       resolve(body); 
      } 
     }); 
    }) 

} 

編輯編輯 我的方式cal及L這些功能是這樣的:

getPlaylists(access_token) 
     .then(playlists => Promise.all(playlists.map(playlist => 
     getArtists(playlist, access_token))) 
     .then(artists => { 

     artists.map(artist => { 
      artist.map(a => { 
      console.log(a); 
      let component = renderToString(
       <App> 
        <Table artists={a} /> 
       </App> 
      ); 

      res.send(
       component 
      ) 
      }) 


     }) 

     })); 

它只返回的第一個結果 - 很明顯,因爲它只能通過foreach循環到達循環一次後,才「res.send()」,讓我怎麼確保它在我渲染視圖之前遍歷所有藝術家?我相信我必須做另一個Promise.all(),但我不確定在哪裏 - 有沒有人有想法?

我很感激:)

+0

我仍然沒有弄清楚問題的解決方案是什麼,但我現在可以說的是,您不會在'artist.map()'函數內返回任何東西。另外,我沒有看到'array.push(obj [key])'中的'key'是什麼。 –

+0

哦,是的,我忘了從測試中刪除變量鍵。我相信我必須做另一個Promise.all在某處讓我的藝術家圖像正確嗎? –

+0

你能解釋一下數據和它的結構嗎?因爲用你的代碼告訴它很混亂。例如'getArtists'似乎會返回一個*(Promise of)*藝術家陣列陣列,而'getArtistImages'會返回一個'img'?持有包含實際圖像的*(可選)*圖像數組 – Thomas

回答

0

如果我正確理解你的問題,這聽起來像你是如何實際使用的Promises結果絆倒了。

異步應用:

Promises封裝的異步結果,這是通過傳遞給then()回調提供。

這個異步ism將在整個應用程序中級聯。

有多種方式的應用管理異步結果的現實:事件,回調,觀測,承諾...

例子(使用事件):

這是原油和來自異步請求的數據如何被注入您的視圖的未經測試的示例。當我的異步請求調用我的then()回調函數時,我更新了觸發事件的模型,以重新呈現我的視圖。如果我不想重新渲染我的整個視圖,怎麼辦?如果我的getArtists()比我的視圖返回的時間快,我的視圖可以呈現它的加載狀態?)。但爲了簡單起見,我們不會去那裏。

+function(){ 


    var _view = $('#viewport'); 
    var _model = {...} 
    var _spotifyClient = new SpotifyClient(); // this contains method similar to those you included in your question 

    _view.on('load', onLoad); 
    _view.on('model:update', onModelUpdate); 

    function onLoad() { 
     _spotifyClient 
     .getArtists() 
     .then(function(result) { 
      // when the getArtists() request has responded, I can update my model. 
      updateModel({ isLoading: false, artists: result }); 
     })   

     // this will happen immediately after starting the "getArtists()" request. 
     udpateModel({ isLoading: true }); 
    } 

    function updateModel(mod) { 
     for(var p in mod) { 
      _model[p] = mod[p]; 
     } 
     _view.trigger('model:update', _model); 
    }   

    function onModelUpdate(model) { 
     refreshView(); 
    } 

    function refreshView() { 
     var viewModel = buildTemplateModel(_model); 
     renderTemplate(_view, viewModel); 
    } 

}(); 

我鼓勵你研究一些視圖框架,如角度,挖空或反應。我也鼓勵你研究Reactive Extensions,它提供了一個可觀察的接口和許多關於異步流的實用工具。對副作用

注:

有一個承諾,觸發事件可以被歸類爲一種「副作用」的結果。在我們的應用程序中,您應該將副作用降到最低,並且它們只屬於應用程序的控制器/主要部分。

如果您在您的應用程序中使用promise,那麼對promise進行操作的可重用的庫類和函數應返回promise。

+0

是的,我還沒有真正地將我的頭包裹在諾言中,我想:)請檢查新的編輯,我想我差不多有 –

0

你需要調用res.send所有結果的陣列上,而不是在每一個單獨:

getPlaylists(access_token).then(playlists => 
    Promise.all(playlists.map(playlist => 
     getArtists(playlist, access_token) 
    )) 
).then(artists => { 
    res.send(artists.map(artist => 
     artist.map(a => { 
      console.log(a); 
      return renderToString(
       <App> 
        <Table artists={a} /> 
       </App> 
      ); 
     }) 
    )) 
}); 

而且你的括號裏略misnested(不匹配then調用的缺口),但沒不會導致問題。

+0

嗨,嗯,它返回一個html數組 - 非常奇怪。 –

+0

恩,是不是你想要的? – Bergi

+0

我想要的就是關於返回所有結果的數組。但是,這會返回整個頁面的數組:如下所示:「

0

它的舊帖子,但我認爲它可以幫助某人。

我寫了一個插件來執行基於承諾支持併發的foreach。我發現你不需要併發性,這使得事情變得更簡單,適用於其他解決方案。

我用你的代碼使用我的插件編寫了一個代碼。有用!

'use strict'; 

var request = require('request') 
var promiseForeach = require('promise-foreach') 

function getPlaylists(access_token) { 

    var options = { 
     url: 'https://api.spotify.com/v1/me/playlists', 
     headers: { 'Authorization': 'Bearer ' + access_token }, 
     json: true 
    }; 

    return new Promise(function (resolve, reject) { 

     request.get(options, function (error, response, body) { 
      var playlists = body.items; 
      var playlistArray = []; 
      playlists.forEach(function (playlist) { 
       var name = playlist.name; 
       var url = playlist.tracks.href; 
       playlistArray.push(url); 
      }); 
      if (!error) { 
       resolve(playlistArray); 
      } else { 
       reject(error); 
      } 
     }); 

    }); 
} 

function getArtists(url, access_token) { 

    var params = { 
     url: url, 
     headers: { 'Authorization': 'Bearer ' + access_token }, 
     json: true 
    }; 

    return new Promise(function (resolve, reject) { 

     request.get(params, function (error, response, body) { 

      var tracks = body.items; 
      var artistArray = []; 
      tracks.forEach(function (artists) { 
       let allArtists = artists.track.artists; 
       allArtists.forEach(function (artist) { 
        artistArray.push(artist); 
       }); 
      }) 

      if (!error) { 

       promiseForeach.each(artistArray, 
        [function (artist) { 
         return getArtistImages(artist.id) 
        }], 
        function (arrayOfResultOfTask, currentList) { 
         return { 
          artistId: currentList.id, 
          artistName: currentList.name, 
          artistImages: arrayOfResultOfTask[0].images 
         } 
        }, 
        function (err, newList) { 
         if (err) { 
          console.error(err) 
          return; 
         } 
         resolve(newList) 
        }) 

      } else { 
       reject(error); 
      } 

     }); 

    }) 

} 

function getArtistImages(artistId) { 
    var options = { 
     url: 'https://api.spotify.com/v1/artists/' + artistId, 
     headers: { 'Authorization': 'Bearer ' + access_token }, 
     json: true 
    }; 

    return new Promise(function (resolve, reject) { 
     request.get(options, function (error, response, body) { 
      if (error != null) { 
       reject(error); 
      } else { 
       resolve(body); 
      } 
     }); 
    }) 

} 

var access_token = 'YOUR-TOKEN'; 


getPlaylists(access_token) 
    .then(playlists => { 

     promiseForeach.each(playlists, 
      [function (playlist) { 
       return getArtists(playlist, access_token) 
      }], 
      function (arrayOfResultOfTask, currentList) { 
       return { 
        playlistURL: currentList, 
        artist: arrayOfResultOfTask[0] 
       } 
       //return renderToString(
       // <App> 
       //  <Table artists={render} /> 
       // </App> 
       //); 
      }, 
      function (err, newList) { 
       if (err) { 
        console.error(err) 
        return; 
       } 
       res.send(newList) 
      }) 

    }); 

插件:https://www.npmjs.com/package/promise-foreach

我希望它可以幫助別人!