![](/img/trans.png)
[英]Node.js Promises within promises not waiting for for loop to return data
[英]Return data from node promises
我目前正在嘗試使用承諾從 Spotify API 獲取數據,昨天我在另一個問題上得到了巨大的幫助,關於同一主題:“ 循環從節點承諾返回的對象並提供給下一個 .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);
}
});
})
}
EDIT EDIT我調用這些函數的方式是這樣的:
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
)
})
})
}));
它只返回第一個結果——顯然是因為它只能在“res.send()”之前循環一次 forEach 循環,那么在渲染視圖之前,我如何確保它循環遍歷所有藝術家? 我相信我必須再做一次 Promise.all(),但我不確定在哪里 - 有沒有人有想法?
我很感激 :)
它的舊帖子,但我認為它可以對某人有所幫助。
我寫了一個插件來基於支持並發的承諾來執行 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
我希望它可以幫助某人!
如果我正確理解您的問題,聽起來您似乎對如何實際使用Promises
的結果感到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);
}
}();
我鼓勵您研究一些視圖框架,例如 angular、knockout 或 react。 我還鼓勵你研究 Reactive Extensions,它提供了一個可觀察的接口和許多關於異步流的實用程序。
副作用注意事項:
承諾的結果觸發事件可以歸類為“副作用”。 您應該在我們的應用程序中將副作用保持在最低限度,並且它們實際上只屬於您的應用程序的控制器/主要部分。
如果您在應用程序中使用了 Promise,則對 Promise 進行操作的可重用庫類和函數應該返回 Promise。
您需要對所有結果的數組調用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
調用的縮進不匹配),但這並沒有導致問題。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.