从节点promises

时间:2016-10-01 13:55:21

标签: javascript node.js

我目前正试图通过承诺从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);
          }
      });
    })

}

编辑编辑 我称这些函数的方式是这样的:

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


        })

      }));

它只返回第一个结果 - 显然是因为它只能在&#34; res.send()&#34;之前循环遍历forEach循环,所以我如何确保它遍历所有艺术家,在渲染视图之前?我相信我必须做另一个Promise.all(),但我不知道在哪里 - 有没有人有想法?

我很感激:)

3 个答案:

答案 0 :(得分:1)

它的老帖子,但我认为它对某人有帮助。

我根据支持并发的承诺编写了一个插件来执行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

我希望它有所帮助!

答案 1 :(得分:0)

如果我理解你的问题,听起来好像你正在试图如何真正使用Promises的结果。

异步应用程序:

Promises封装了一个异步结果,该结果通过传递给then()的回调提供。

此异步主题将在整个应用程序中级联。

应用程序可以通过多种方式管理异步结果的实际情况:事件,回调,可观察性,承诺......

示例(使用事件):

这是一个原始且未经测试的示例,说明如何将异步请求中的数据注入到视图中。当我的异步请求调用我的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,它提供了一个可观察的接口和许多关于异步流的实用程序。

关于副作用的注意事项:

具有承诺触发事件的结果可被归类为&#34;副作用&#34;。您应该在我们的应用程序中将副作用保持在最低限度,并且它们实际上只属于应用程序的控制器/主要部分。

如果你在你的应用程序中使用promises,那么可以使用promises的可重用库类和函数应该返回promises。

答案 2 :(得分: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>
            );
        })
    ))
});

此外,您的括号内容略有错误(与then来电的缩进不匹配),但这并未导致问题。