无法在递归承诺函数中解决

Unable to resolve within recursive promise function

我想我从根本上误解了 Promises 在 Node 中的工作方式。

我正在编写一个非常小的 Express 应用程序,我想 return JSON 我所有包含特定关键字的 Spotify 播放列表。我正在使用来自 npm 的 spotify-web-api-node

Spotify 的 API 限制每个请求 50 个播放列表。我有(我认为的)解决方案:只需增加偏移值,直到您不再需要为止。

我的函数如下所示(playlists 在路由函数上方声明):

app.get('/playlists', function(req, res) {
    addPlaylistsToList()
        .then((data) => res.json(data))
});

function addPlaylistsToList(offset) {
    if (!offset) { offset = 0; }
    var limit = 50;
    return new Promise((resolve, reject) => {
        spotifyApi.getUserPlaylists(config.USER, { limit: limit, offset: offset })
            .then(function(data) {
                playlists.push(data.body.items.filter((playlist) => playlist.name.includes(config.TITLE_CONDITION)));
                if (data.body.items.length == limit) {
                    addPlaylistsToList(offset + limit);
                } else {
                    let result = [...new Set(playlists)];
                    resolve(result);
                }
            })
    });
}

我不能在这里打电话给 resolve 吗?我的意思是,我知道我不能,因为我陷入了无限循环。是否有 "best practice" re: 递归承诺?我在滥用 then 吗?

使用递归承诺通常没问题,但您在实施过程中遗漏了一些东西。每次调用 addPlaylistsToList() 都会创建一个新的承诺,但只有一次调用会创建一个实际解决的新承诺:最后一个。所有其他创建的承诺将无限期待定。

您可以用更典型的递归风格重写函数,如下所示:

function addPlaylistsToList(offset = 0) { //default parameter value to 0
    var limit = 50;
    return spotifyApi.getUserPlaylists(config.USER, { limit: limit, offset: offset })
        .then(function(data) {
            playlists.push(data.body.items.filter((playlist) => playlist.name.includes(config.TITLE_CONDITION)));
            if (data.body.items.length == limit) {
                return addPlaylistsToList(offset + limit); //return a promise
            } else {
                return [...new Set(playlists)]; //return the final value
            }
        });
}

(未经测试)

这是有效的,因为向 .then() 回调返回承诺会导致 .then() 到 "follow" 返回的承诺 - 它只会在 that[=26] 时解析=] 承诺解决,等等。您可以像这样递归地链接 promises,最终最底部的一个将用您的结果数组解析,导致所有其他的也用该数组解析。

另外,使用 async/await:

可以写得更简洁
async function addPlaylistsToList(offset = 0) { //default parameter value to 0
    var limit = 50;
    var data = await spotifyApi.getUserPlaylists(config.USER, { limit: limit, offset: offset });
    playlists.push(data.body.items.filter((playlist) => playlist.name.includes(config.TITLE_CONDITION)));
    if (data.body.items.length == limit) {
        return await addPlaylistsToList(offset + limit);
    } else {
        return [...new Set(playlists)];
    }
}