关于 Promise 的重试
Erioifpud
1 min read
这段时间经常用到重试功能,因为一些需求实现起来需要频繁访问接口,但这个接口不是我们自己的,服务器在海外,所以访问起来失败率肉眼可见地高,所以我需要在访问失败(因为连接原因)时自动进行重试。
既然常用,那就把他记下来,方便下次查阅。
实现
先准备一个假的 fetch
,在文章里面我不会真去访问这个接口,就拿假 fetch
来当例子:
const fetch = (url) => {
return new Promise((resolve, reject) => {
const r = Math.random()
const success = r > 0.5
if (success) {
resolve(r)
} else {
console.log(`${url}: ${r}`)
reject(new Error('连接错误'))
}
})
}
每次调用 fetch
都有 50% 的几率会失败,就把他当作连接失败吧。
接着是对 fetch
的包装,fetch
中是没有重试功能的,所以要做一个 wrapper
函数去调控他,判断请求是否成功,不成功就走重试逻辑:
function fetchWithRetry(url, retries = 3, delay = 3000) {
return new Promise((resolve, reject) => {
const wrapper = (n) => {
fetch(url)
.then(response => {
// 这里处理一些非连接原因的错误
// if (!response.ok) {
// throw new Error('Fetch error');
// }
resolve(response);
})
.catch(error => {
if (n > 0) {
setTimeout(() => wrapper(n - 1), delay);
} else {
reject(error);
}
});
};
wrapper(retries);
});
}
可以看到 fetchWithRetry
又给包了一层 Promise
,fetch
在 wrapper
函数中,参数 n
表示剩余重试的次数。
先调用 wrapper
发起一次请求,然后当请求成功时正常 resolve
响应数据,请求出错的时候先判断还有没有重试次数,有就重新调用 wrapper
再次请求。
如果有一系列请求需要重试的话,使用 Promise.allSettled
就好了,相比于 Promise.all
,allSettled
会在所有 Promise
完成(无论失败与否)后变成 fulfilled
,他没有 rejected
状态,可以在 results
中判断是哪个请求失败了。
const urls = ['<https://example1.com>', '<https://example2.org>', '<https://example3.net>'];
Promise.allSettled(urls.map(url => fetchWithRetry(url)))
.then(results => {
results.forEach((result, i) => {
if (result.status === 'fulfilled') {
console.log(`Request to ${urls[i]} succeeded`);
} else {
console.log(`Request to ${urls[i]} failed with ${result.reason}`);
}
});
});
0
Subscribe to my newsletter
Read articles from Erioifpud directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by