如何等待所有其他并行承诺解决?

问题描述 投票:0回答:1

我目前正在为网站实现客户端令牌刷新脚本,当需要刷新访问令牌时遇到了一个小问题。

对于某些页面,客户端从服务器获取多个(假设是两个)文档(这两个文档都需要通过授权请求获取,即使用访问令牌),因此当客户端访问令牌过期时,会调用此同时获取两次令牌刷新操作,客户端保存了最后一次结束的刷新操作的最终访问令牌。这是资源的浪费,因为第一个访问令牌从未被使用,甚至从未保存到客户端。

为了解决这个问题,我做了刷新令牌创建临时

localStorage
条目的过程:

async function getAccessToken(auth) {
  // Check if the current token is valid, and return it if so
  ...
  // Case when token needs to be refreshed
  localStorage.setItem("TOKEN_PENDING", "TOKEN_PENDING");
  // Refresh the token
  ...
  const res = await fetch(...);
  ...
  // Clean up after refresh
  localStorage.removeItem("TOKEN_PENDING");
  // Return the new token
  ...
}

显然,我现在需要检查在刷新令牌之前是否已创建此条目:

const isTokenPending = () => localStorage.getItem("TOKEN_PENDING") != null;

async function getAccessToken(auth) {
  // Check if the current token is valid, and return it if so
  ...
  // Case when token needs to be refreshed
  localStorage.setItem("TOKEN_PENDING", "TOKEN_PENDING");

  // Check if the token is currently being refreshed in a parallel instance of the function
  if (isTokenPending()) {
    while (isTokenPending()) {
      await new Promise((resolve) => setTimeout(resolve, 0));
    }
    console.info("Token finished refreshing");
    // Recursively call the function, which will now produce the new valid token
    return getAccessToken(auth);
  }

  localStorage.setItem("TOKEN_PENDING", "TOKEN_PENDING");
  // Refresh the token
  ...
  const res = await fetch(...);
  ...
  // Clean up after refresh
  localStorage.removeItem("TOKEN_PENDING");
  // Return the new token
}

有问题的部分是当函数检测到临时条目存在时,并等待直到令牌在函数的其他实例中刷新。目前,我已经尝试了

while
循环中看到的方法:

while (isTokenPending()) {
  await new Promise((resolve) => setTimeout(resolve, 0));
}

但是,我不确定这是否是一个足够好的解决方案,因为它看起来很老套,而且乍一看并不明显发生了什么。我使用了一个自我解决的承诺,它在 0 毫秒超时后解决,这似乎没有做任何事情。然而,等待 Promise 的事实允许程序“放弃”异步处理,并同时处理正在刷新令牌的并行实例,从而允许

while
循环最终结束。

我还尝试删除 hackish 0 毫秒承诺,但随后该过程消耗了所有处理时间并且不允许并行实例刷新令牌。这导致网站挂起/冻结。

我希望该函数的两次调用并行运行,并且

while
循环会等待第二次调用完成,这将使对
isTokenPending()
的下一次调用返回
false

另外,我发现了这个帖子: 如何等待 JavaScript Promise 解析后再恢复功能?

但是,接受的答案表明没有办法实现这一点,而且它的日期是 9 年前,所以如果这甚至适用于我的问题,我想知道自那以后是否有任何变化。也许使用

yield

所以我真正要问的是,如何允许这部分代码(带有

while
循环)等待异步代码完成,然后在没有未解决的承诺时继续?

预先感谢您提出的解决方案。

javascript asynchronous async-await promise
1个回答
0
投票

我知道你想要什么,但我觉得它可能比需要的更复杂一点。

假设您的所有请求都使用相同的令牌,您可以在执行任何文档获取请求之前检查令牌的有效性。

这确保只完成一次刷新请求,并且所有文档获取请求都具有有效的令牌。我在想类似的事情

async validateToken() { if(!isTokenValid) { const refreshedToken = await refrehToken(); storeUpdatedToken(refreshedToken) return refreshedToken; } return getStoredValidToken(); } async function fetchDocuments(documentToFetch) { const validToken = await validateToken() for(let i = 0; i < documentToFetch; i ++) { await fetchDocument(validToken); } }
现在,此代码将依次运行所有 fetchDocument 请求,这可能不是您想要的。

这就是

Promise.all

 的用武之地。该函数接受一组待处理的 Promise,只有当所有 Promise 都得到解决时才会得到解决。因此,您可以构建一个包含待处理承诺的数组,每个要获取的文档对应一个数组,然后 await
 
Promise.all
 函数。

/* ... */ async function fetchDocuments(documentToFetch) { const validToken = await validateToken() const pendingPromises = [] for(let i = 0; i < documentToFetch; i ++) { pendingPromises.push(fetchDocument(validToken)); } await Promise.all(pendingPromises) }
现在,所有文档获取请求都将并行运行,只有在获取所有文档后,

fetchDocuments

函数才会解析。

© www.soinside.com 2019 - 2024. All rights reserved.