You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

递归拼接字符串异常:仅返回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");

核心问题原因

  1. 字符串的不可变性:JavaScript中字符串是不可变类型,递归调用时传递给子函数的是当前csvData的副本。子函数里对csvData的拼接操作,本质是创建了一个新的字符串,并不会修改父函数中csvData的原始值。
  2. 递归返回值未接收:当存在下一页数据时,代码仅调用了await repeatGet(...),但没有将子函数返回的、已经追加了新内容的字符串赋值给当前函数的csvData。因此当前函数最终返回的还是自己拼接完当前页数据后的字符串,不会包含子函数获取的后续页内容。
  3. 日志逆序的原因:递归执行逻辑是先逐层深入到最后一页(最内层递归),然后从最内层开始逐步返回结果。所以控制台会先输出最后一次调用的日志,再往前输出之前的调用日志,呈现逆序的效果。

修复后的代码

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 04:52:11