Javascript 获取 API 太快;在第二个 HTTP(获取 api)请求之前完成

Javascript Fetch API too fast; finishes before second HTTP (fetch api) request

我有 2 个 HTTP 请求使用 Java 脚本中的 Fetch API。我的主要语言是 Java 但我负责一个前端项目,所以我不确定是否有一个简单的解决方案。我的问题是第一个调用(命中随机服务器)命中的外部端点不同于第二个外部端点(此端点在 Azure 上),但第二个端点依赖于第一个端点。基本上,第一个端点是 POST 请求 creates/populates Azure 网站(IoT 中心)上的 object/IoT 设备。因此,在设备出现在网站上之前,第二个请求实际上无法执行任何操作(在本例中为 PATCH)。我注意到它通常需要几秒钟,比如 1-5 秒才会出现。

我的 Fetch API 看起来像这样:

fetch('https://first-API-endpoint-on-random-server.com/creates-thing-on-Azure', {
  method: 'POST',
  headers: myHeaders,
  body: JSON.stringify(data),
})
.then(response => response.json())
.then(data => {
  console.log('Success:', data);

  // Beginning of nested Fetch
  return fetch('https://second-DIFFERENT-API-endpoint-on-azure.com/tries-to-edit-thing-on-Azure', {
      method: 'PATCH',
      headers: myHeaders,
      body: JSON.stringify(data),
    })
    .then(response => response.json())
    .then(data => {
      console.log('Success:', data);
    })
    .catch((error) => {
      console.error('Error:', error);
    });
})

所以基本上你点击我网站上的一个按钮 UI 并且它应该 运行 这个 onClick 函数和 create/populate Azure 上的 object/IoT 设备通过第一次获取API 调用,然后第二个 Fetch API 调用应该 "PATCH" 它或编辑 JSON.

第一个 API 调用 总是 有效 但问题是第二个 API 调用似乎总是因 404 "device not found" 错误,因为我认为第一个调用完成得太快,然后第二个调用试图修补网站上什至不存在的东西。我的解决方案是将 out/split 和 API 调用划分为两个不同的点击事件,这解决了用户点击第二个按钮时的问题,即 2nd Fetch API call/PATCH 通常总是成功。

我真的很想将它们组合成一个函数,这样用户就永远不会知道 PATCH event/http 调用。我怎样才能让这第二个 Fetch 真正起作用?

我可以提供您想要的任何详细信息,谢谢

看起来服务器对初始请求的 200 响应表明请求已正确处理,但并未表明资源已在 Azure 上创建。资源是在服务器响应后的一段时间内在 Azure 上创建的,但是客户端的第二个请求是在资源创建之前 的某个时间发出的。以下是可能的请求和响应序列的时间表:

  1. 客户端(Web 浏览器)向服务器发出第一个 (POST) 请求。
  2. 服务器收到请求,向Azure发出创建资源的请求,returns一个200表示请求已经处理。此时,Azure 已收到请求并可能开始创建资源。
  3. 客户端(Web 浏览器)收到来自服务器的 200 响应并继续发出第二个 (PATCH) 请求。此时,Azure 可能正在创建资源,但尚未完成创建,因此它 returns a 404.

这个问题可以在后端解决,例如让服务器等待在 Azure 上创建资源,然后再用 200 响应客户端。如果那不可能,可以在客户端单独或组合采取两条措施:

  • 在收到第一个请求 200 后,等待一段固定的时间再发出第二个请求。由于我们没有来自后端的关于何时创建资源的指示,我们可以选择任意时间。这是我们等待 1 秒的示例:
function createResource(data) {
  return fetch('https://first-API-endpoint-on-random-server.com/creates-thing-on-Azure', {
    method: 'POST',
    headers: myHeaders,
    body: JSON.stringify(data),
  })
}

function updateResource(data) {
  return fetch('https://second-DIFFERENT-API-endpoint-on-azure.com/tries-to-edit-thing-on-Azure', {
     method: 'PATCH',
     headers: myHeaders,
     body: JSON.stringify(data),
  })
}

function createAndUpdateResource(data) {
  return new Promise((resolve, reject) => {
    return createResource(data)
      .then(res => res.json())
      .catch(reject)
      .then(resourceData => {
        setTimeout(() => {
          // do we pass data or resourceData data to the PATCH request?
          updateResource(resourceData)
            .then(res => res.json())
            .then(resolve)
            .catch(reject)
          });
        }, 1000);
      });
  });
}

createAndUpdateResource({ name: "abc" })
  .then(res => {
    console.log("success:", res);
  })
  .catch(err => {
    console.log("error: ", err.message);
  });

  • 对第二个请求使用重试机制。在获得 404 时,我们可以对同一请求进行有限次数的重试。这可以与上述超时方法结合使用。这是重试的示例实现:
function withRetry(apiCall, retryCount, timeoutMS, statusForRetry) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      apiCall()
        .then(res => {
          if (!res.ok) {
            if (res.status === statusForRetry && retryCount > 0) {
              return withRetry(
                apiCall,
                retryCount - 1,
                timeoutMS,
                statusForRetry
              );
            }
            reject(new Error("couldn't update"));
          }
          return res.json();
        })
        .then(resolve)
        .catch(reject);
    }, timeoutMS);
  });
}

function createAndUpdateResource2(data) {
  return new Promise((resolve, reject) => {
    return createResource(data)
      .then(res => res.json())
      .then(resourceData =>
        withRetry(
          () => updateResource(resourceData),
          3, // 3 retries
          1000, // wait 1 second
          404 // retry on 404
        )
      )
      .then(resolve)
      .catch(reject);
  });
}