发出 http 请求然后可能发出另一个请求的最佳方法

Best way to make a http request and then potentially another one

标题不是很清楚,但要详细说明,我需要向 API 端点发出 HTTP 请求,到目前为止,我使用的函数看起来像这样:

function getPostsFromAPI(argOne, argTwo) {
    const apiUrl = `https://www.exampleapi.com/v1/userposts`
    apiGet(`${apiUrl}?name=argOne&something=argTwo`).then(userPosts => {
        // do stuff with userPosts
        return userPostData
    }).catch(handleError)
}

但是,API 响应可以包括以下内容:

{
    //...
    "has_more": true,
    "next_offset": 10
}

在这种情况下,我需要再次发送 API 调用,这次使用 &offset=10 参数。

Promise 需要继续进行 API 调用,直到 has_more: true 不再存在。我最初的想法是仅 re-run getPostsFromAPI() 基于内部的 if 语句,但我无法弄清楚如何使它在承诺中干净利落地工作。最终,promise 应该继续发出请求,直到 API 说它 运行 没有数据可以提供(我将实施我自己的限制)。

实现此目标的最佳方法是什么?

如果使用 async/await,实现此目的的算法会更加明显。您可以只创建一个空数组,然后在循环中逐渐附加到它,直到服务器指示没有更多结果。

async function getPostsFromAPI(argOne, argTwo) {
    const apiUrl = `https://www.exampleapi.com/v1/userposts`

    let results = [];
    let offset = 0;

    while (true) {
        let response = await apiGet(`${apiUrl}?name=argOne&something=argTwo&offset=${offset}`);
        results = results.concat(response.records);
        if (response.has_more) {
            offset = response.next_offset;
        } else {
            return results;
        }
    }
}

如果您不能使用 async/await 并且必须遵守承诺,您可以使用递归让方法在每次响应指示有更多记录时调用自身:

function getPostsFromAPI(argOne, argTwo) {
    return new Promise((resolve, reject) => {
        const apiUrl = `https://www.exampleapi.com/v1/userposts`;

        let results = [];
        let offset = 0;

        const getNextPage = (offset = 0) => {
            apiGet(`${apiUrl}?name=argOne&something=argTwo&offset=${offset}`).then((response) => {
                results = results.concat(response.records);
                if (response.has_more) {
                    getNextPage(response.next_offset);
                } else {
                    resolve(results);
                }
             }).catch(reject);
        }

        getNextPage(0);
    });
}

请注意,作为一般的良好做法,您永远不应通过连接或模板字符串构造查询字符串。您应该使用 URLSearchParams.toString() 来确保您的查询字符串被正确编码。您可以通过创建一个新的 URL:

来间接执行此操作
const url = new URL(`https://www.exampleapi.com/v1/userposts`)

url.searchParams.append("argOne", argOne);
url.searchParams.append("argTwo", argTwo);
url.searchParams.append("offset", offset);

url.toString()

这是 async generator 的一个很好的用例。

看起来像下面这样

async function* getPostsFromAPI(arg1, arg2) {
  const apiUrl = `https://www.exampleapi.com/v1/userposts`
  let response = { next_offset: 0 };

  do {
    response = await apiGet(`${apiUrl}?name=${arg1}&something=${arg2}&offset=${response.next_offset}`)

    response.items.forEach((item) => {
      yield item
    })

  } while (response.has_more)
}