如何使用 js 的 Promise 实现 Fetch 请求超时自动重试 All In One
如何使用 js 的 Promise 实现 Fetch 请求超时自动重试 All In One
Promise 请求超时重试 / Promise 请求超时重连
"use strict";
/**
*
* @author xgqfrms
* @license MIT
* @copyright xgqfrms
* @created 2020-11-20
* @modified 2022-03-20
*
* @description
* @description
* @difficulty Medium
* @complexity O(n)
* @time O(n)
* @augments
* @example
* @link
* @solutions
*
* @best_solutions
*
*/
const log = console.log;
const autoRefetch = (url = ``, times = 3) => {
const promise = fetch(url);
promise.then((res) => {
// return res.json();
Promise.resolve(res.json());
}, (err) => {
if(times > 0) {
times -= 1;
promise = fetch(url);
}
}).catch(err => {
Promise.reject(err);
})
return promise;
}
autoRefetch(`https://cdn.xgqfrms.xyz/json/badges.js`)
.then(res => console.log('json =', res.json()))
.catch(err => console.error(`err =`, err))
.finally(() => {
console.log('whatever close loading...');
});
fix: Uncaught (in promise) ❌
function maxRequest(url = ``, times = 3) {
// 1. 闭包,保存私有属性
function autoRetry (url, times) {
console.log('times = ', times);
times--;
// 2. fetch 本身返回值就是 Promise,不需要再次使用 Promise 包裹
return fetch(url).then(value => {
if(value.status === 200) {
console.log(`✅ OK`, value);
// 3. 手动返回 Promise 的 value, 没有返回值 默认返回 undefined
return value;
} else {
throw new Error(`❌ http code error: ${value.status }`);
}
}).catch((err) => {
console.log(`❌ Error`, err);
if (times < 1) {
// 4. 方便后续的 thenable 处理 error
throw new Error('💩 over max request times!');
} else {
// 5. 返回递归方法
return autoRetry(url, times);
}
});
}
// 6. 返回一个 Promise 的结果 (成功 Promise 或失败 Promise)
return autoRetry(url, times);
}
// error test case
maxRequest(`https://cdn.xgqfrms.xyz/json/badges.js`)
.then(res => res.json())
.then(json=> console.log('json =', json))
.catch(err => console.error(`err =`, err))
.finally(() => {
console.log('👻 whatever close loading...');
});
// sucess test case
maxRequest(`https://cdn.xgqfrms.xyz/json/badges.json`)
.then(res => res.json())
.then(json=> console.log('json =', json))
.catch(err => console.error(`err =`, err))
.finally(() => {
console.log('👻 whatever close loading...');
});
Promise 原理分析
then
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises
thenable
function maxRequest(url = ``, times = 3) {
// closure
function autoRetry (url, times) {
console.log('times = ', times);
times--;
// No more `new Promise`, just a chain.
return fetch(url).then(value => {
if(value.status === 200) {
console.log(`✅ `, value);
return value;
} else {
throw new Error(`❌ http code error: ${value.status }`);
}
}).catch((err) => {
console.log(`❌ Error`, err);
if (times < 1) {
// Converted to rejection. You could still throw a
// string, but throwing an Error is more idiomatic.
throw new Error('💩 over max request times!');
} else {
// Make sure to return your Promise here. Don't worry
// about returning a Promise; it's automatically wrapped
// like Promise.resolve() does, so this chained-fetch()
// Promise eventually gets the same result as the returned
// recursive Promise.
return autoRetry(url, times);
}
});
}
return autoRetry(url, times);
}
maxRequest(`https://cdn.xgqfrms.xyz/json/badges.js`)
.then(res => res.json())
.then(json => {
console.log('json =', json);
return json;
}, err => {
console.log('error =', err);
throw new Error(err);
})
.catch(err => console.log(`err =`, err))
.finally(() => {
console.log('whatever close loading...');
});
try
function maxRequest(url = ``, times = 3) {
// 闭包
function autoRetry (url, times) {
console.log('times = ', times);
times--;
return new Promise((resolve, reject) => {
fetch(url).then(value => {
if(value.status === 200) {
console.log(`✅ OK`, value);
resolve(value);
} else {
throw new Error(`❌ http code error: ${value.status }`);
}
}).catch((err) => {
console.log(`❌ Error`, err);
if (times < 1) {
reject('💩 over max request times!');
} else {
return autoRetry(url, times);
}
});
});
}
return autoRetry(url, times);
}
// maxRequest(`https://cdn.xgqfrms.xyz/json/badges.js`);
maxRequest(`https://cdn.xgqfrms.xyz/json/badges.js`)
.then(res => res.json())
.then(json=> console.log('json =', json))
.catch(err => console.error(`err =`, err))
.finally(() => {
console.log('whatever close loading...');
});
function maxRequest(url = ``, times = 3) {
// 闭包
function autoRetry (url, times) {
console.log('times = ', times);
times--;
return fetch(url).then(value => {
if(value.status === 200) {
console.log(`✅ OK`, value);
return value;
} else {
throw new Error(`❌ http code error: ${value.status }`);
}
}).catch((err) => {
console.log(`❌ Error`, err);
if (times < 1) {
throw new Error(`💩 over max request times!\n${err}`);
} else {
return autoRetry(url, times);
}
});
}
return autoRetry(url, times);
}
// maxRequest(`https://cdn.xgqfrms.xyz/json/badges.js`);
maxRequest(`https://cdn.xgqfrms.xyz/json/badges.js`)
.then(res => res.json())
.then(json=> console.log('json =', json))
.catch(err => console.error(`err =`, err))
.finally(() => {
console.log('whatever close loading...');
});
solutions
function maxRequest(url = ``, times = 3) {
// 闭包
function autoRetry (url, times) {
console.log('times = ', times);
times--;
return new Promise((resolve, reject) => {
Promise.resolve(fetch(url)).then(value => {
if(value.status === 200) {
console.log(`✅ OK`, value);
resolve(value);
} else {
throw new Error(`❌ http code error: ${value.status }`);
}
}).catch((err) => {
console.log(`❌ Error`, err);
if (times < 1) {
reject('💩 over max request times!');
} else {
autoRetry(url, times);
}
});
});
}
return autoRetry(url, times);
}
maxRequest(`https://cdn.xgqfrms.xyz/json/badges.json`)
.then(res => res.json())
.then(json => {
console.log('json =', json);
});
function maxRequest(url = ``, times = 3) {
// 闭包
function autoRetry (url, times) {
console.log('times = ', times);
times--;
return new Promise((resolve, reject) => {
Promise.resolve(fetch(url)).then(value => {
if(value.status === 200) {
console.log(`✅ OK`, value);
resolve(value);
} else {
throw new Error(`❌ http code error: ${value.status }`);
}
}).catch((err) => {
console.log(`❌ Error`, err);
if (times < 1) {
reject('💩 over max request times!');
} else {
autoRetry(url, times);
}
});
});
}
return autoRetry(url, times);
}
maxRequest(`https://cdn.xgqfrms.xyz/json/badges.js`);
maxRequest(`https://cdn.xgqfrms.xyz/json/badges.json`)
.then(res => res.json())
.then(json=> console.log('json =', json))
.catch(err => console.error(`err =`, err))
.finally(() => {
console.log('whatever close loading...');
});
模拟 Promise.all & Promise.allSettled
Promise.all
要么全部 promise 结果都成功了,返回全部的 promise 构成的一个结果值的数组;
要么只要有一个 promise 失败了,就返回失败了的 promise 的 error 值,默认 undefined
一句话总结: 全部 promise 结果都成功了,返回一个有所有成功的结果值的数组; 只要有一个promise 失败了,就的返回失败结果;
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all
PromiseAll = function(promises) {
const values = [];
let count = 0;
return new Promise((resolve, reject) => {
promises.forEach((promise, index) => {
// promise.then ? 强制转换
Promise.resolve(promise).then(res => {
count += 1;
values[index] = res;
if (count === promises.length) {
resolve(values);
}
}, err => {
reject(err);
})
})
})
}
// pending...
Promise.allSettled 返回全部的 promise 的结果,无论 promise 结果是成功还是失败,构成一个可迭代的数组对象
成功的 promise 返回一个有 status: 'fulfilled' 和 value 构成的对象
失败的 promise 返回一个有 status: 'rejected' 和 reason 构成的对象
一句话总结: 无论 promise 是成功了还是失败了, 最终都返回一个有 status 和 value 或 reason 构成的对象数组;
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/allSettled
demos
PromiseAllSellted = function(promises) {
const result = [];
let count = 0;
return new Promise((resolve, reject) => {
// promise.then ? 强制转换
promises.forEach((promise, index) => {
Promise.resolve(promise).then(res => {
result[index] = {
status: `fullfilled`,
value: res,
}
}, err => {
result[index] = {
status: `rejected`,
reason: err,
}
}).finally(() => {
count++
if (count === promises.length) {
resolve(result)
}
})
})
})
}
refs
Promise.allSettled & Promise.all & Promise.race & Promise.any All In One
https://www.cnblogs.com/xgqfrms/p/13414614.html
©xgqfrms 2012-2025
www.cnblogs.com/xgqfrms 发布文章使用:只允许注册用户才可以访问!
原创文章,版权所有©️xgqfrms, 禁止转载 🈲️,侵权必究⚠️!
本文首发于博客园,作者:xgqfrms,原文链接:https://www.cnblogs.com/xgqfrms/p/14016391.html
未经授权禁止转载,违者必究!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异
2019-11-21 HTML5 Server-Sent Events
2019-11-21 Learning React in depth with React Official Documents
2019-11-21 js in depth & prototype & __proto__
2018-11-21 Access-Control-Allow-Methods: OPTIONS & CORS All In One
2015-11-21 IcoMoon 图标字体 Custom built and crisp icon fonts, done right
2015-11-21 blog 社会化评论插件 多说for china, disqus for global range
2015-11-21 在线的代码托管平台 coding.net ===中国扩展版github