递归拼接字符串异常:仅返回Spotify API首次调用的CSV数据
问题分析:递归拼接CSV字符串仅返回首次API调用结果的原因
场景说明
原本通过递归调用Spotify API获取用户收藏歌曲数据,用数组存储所有结果再转换为CSV的代码运行正常。改为直接在递归中拼接CSV字符串后,最终仅返回首次API调用生成的内容,调试时发现每次递归调用里字符串确实会追加内容,但控制台会逆序输出各阶段日志,且日志中字符串长度逐步增长。
原正常代码(数组存储版)
async function repeatGet(url, tracksArray = [["Track", "Artist", "Album", "Date-Time Added", "Spotify Link"]]) { tracksObject = await apiCall(url); for (track of tracksObject.items) { tracksArray.push([track.track.name, track.track.artists[0].name, track.track.album.name, track.added_at, track.track.external_urls.spotify]) } if (tracksObject.next != undefined) { await repeatGet(tracksObject.next, tracksArray) } return tracksArray } tracksArray = await repeatGet("https://api.spotify.com/v1/me/tracks?limit=50") console.log(tracksArray); let csvData = "sep=\t \n"; tracksArray.forEach(row => csvData += row.join("\t") + "\n") console.log(csvData);
有问题的代码(直接拼接CSV版)
async function repeatGet(url, csvData = "sep=\t \nTrack\tArtist\tAlbum\tDate-Time Added\tSpotify\tLink") { tracksObject = await apiCall(url); for (track of tracksObject.items) { csvData += "\n" + track.track.name + "\t" + track.track.artists[0].name + "\t" + track.track.album.name + "\t" + track.added_at + "\t" + track.track.external_urls.spotify; } if (tracksObject.next != undefined) { await repeatGet(tracksObject.next, csvData) } console.log(csvData,csvData.length); return csvData; } csvData = await repeatGet("https://api.spotify.com/v1/me/tracks?limit=50");
核心问题原因
- 字符串的不可变性:JavaScript中字符串是不可变类型,递归调用时传递给子函数的是当前
csvData的副本。子函数里对csvData的拼接操作,本质是创建了一个新的字符串,并不会修改父函数中csvData的原始值。 - 递归返回值未接收:当存在下一页数据时,代码仅调用了
await repeatGet(...),但没有将子函数返回的、已经追加了新内容的字符串赋值给当前函数的csvData。因此当前函数最终返回的还是自己拼接完当前页数据后的字符串,不会包含子函数获取的后续页内容。 - 日志逆序的原因:递归执行逻辑是先逐层深入到最后一页(最内层递归),然后从最内层开始逐步返回结果。所以控制台会先输出最后一次调用的日志,再往前输出之前的调用日志,呈现逆序的效果。
修复后的代码
async function repeatGet(url, csvData = "sep=\t \nTrack\tArtist\tAlbum\tDate-Time Added\tSpotify Link") { // 声明变量避免全局污染 const tracksObject = await apiCall(url); for (const track of tracksObject.items) { csvData += "\n" + track.track.name + "\t" + track.track.artists[0].name + "\t" + track.track.album.name + "\t" + track.added_at + "\t" + track.track.external_urls.spotify; } if (tracksObject.next != undefined) { // 接收递归返回的更新后的CSV字符串 csvData = await repeatGet(tracksObject.next, csvData); } console.log(csvData, csvData.length); return csvData; } // 用const声明变量,符合代码规范 const csvData = await repeatGet("https://api.spotify.com/v1/me/tracks?limit=50");
修复要点
- 接收递归调用的返回值,将更新后的CSV字符串重新赋值给当前函数的
csvData,确保后续页的内容能合并进来。 - 给
tracksObject、track、csvData加上const/let声明,避免全局变量污染,符合JavaScript代码规范。
内容的提问来源于stack exchange,提问作者scivern
相关产品推荐
相关产品推荐

