我想做的是:从我的单页应用程序中,使用 fetch 从 API 获取数据。如果 JWT 过期,请向 API 请求新的 JWT,然后重新请求数据。
我尝试用 3 个函数来实现它:
getData
调用 fetchData
。如果 fetchData
返回 JWT expired
消息,则调用函数 getjwt
(也在 getdata
内)和 then(仅然后)fetchData
again.
应用程序调用
getData
(在页面加载时——但这与这里无关)
我有一个功能
getjwt
:
async function getjwt() {
// posts a refresh token and gets a jwt in headers
let adresse = `${baseUrl}/auth/getjwt`
let headers = { 'Content-Type': 'application/json' }
postData(adresse, refresh.value, headers)
.then((response) => {
if (response.status === 200) {
jwt.value = response.headers.get('__auth__')
}
})
.catch((err) => alert(err))
}
和功能
fetchData
和getData
:
async function fetchData (url = '', headers = {}) {
let r = await fetch(url, {
method: 'GET',
mode: 'cors',
cache: 'no-cache',
credentials: 'include',
headers: headers,
redirect: 'follow',
referrerPolicy: 'no-referrer',
})
return r
}
async function getData(url = '', headers = {}) {
let r = await fetchData(url, headers)
if (r.status === 409) {
console.log("Oh no! My token has expired.")
getjwt()
let r = await fetchData(url, headers)
return r.json()
}
return r.json()
}
故意地,第一次调用 fetchData 时,jwt 已过期。
getjwt
被调用并且 jwt 得到正确更新(在 Pinia 商店中)。但是:执行顺序是:getData
被调用两次,然后getjwt
被调用。
也许我遗漏了一些关于异步操作和 Promise 的东西,但是我怎么能强制调用
getjwt
并在 getData
第二次被调用之前完成获取新的 jwt。
getJwt
函数的问题在于它返回的承诺不会等待其中的异步操作完成。结果,它返回的承诺得到解决beforepostData
完成。
您可以通过返回
postData
的承诺来解决此问题,如下所示:
async function getjwt() {
...
return postData(...)
.then((response) => {
...
})
.catch((err) => {
...
throw err;
});
}
在
return
调用之前添加一个 postData
可以解决问题,因为 return
确保 getJwt
函数返回的承诺得到 resolved 到 return postData(...)
返回的承诺。一个承诺 p1
解决另一个承诺 p2
只是意味着承诺 p1
将等待 p2
解决(履行或拒绝)。如果 p2
满足,则 p1
以相同的值满足。如果 p2
被拒绝,p1
也会以相同的拒绝原因被拒绝。
注意
catch
方法的变化。需要throw err
来避免隐含地将承诺拒绝转换为承诺实现.
由于您没有在函数内部使用
await
关键字,您不妨从函数签名中删除 async
关键字。
最后,要使其工作,您需要
await
调用getJwt
函数内部的getData
函数。
async function getData(url = '', headers = {}) {
...
if (r.status === 409) {
...
await getjwt()
...
}
...
}
我会通过以下两项改进来重构您的代码:
catch
方法调用,在调用getJwt
函数的函数中处理错误async-await
async function getjwt() {
// posts a refresh token and gets a jwt in headers
let adresse = `${baseUrl}/auth/getjwt`;
let headers = { 'Content-Type': 'application/json' };
const response = await postData(adresse, refresh.value, headers);
if (response.status === 200) {
jwt.value = response.headers.get('__auth__');
}
}