如何使用 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
(🐞 反爬虫测试!打击盗版⚠️)如果你看到这个信息, 说明这是一篇剽窃的文章,请访问 https://www.cnblogs.com/xgqfrms/ 查看原创文章!
©xgqfrms 2012-2021
www.cnblogs.com/xgqfrms 发布文章使用:只允许注册用户才可以访问!
原创文章,版权所有©️xgqfrms, 禁止转载 🈲️,侵权必究⚠️!
本文首发于博客园,作者:xgqfrms,原文链接:https://www.cnblogs.com/xgqfrms/p/14016391.html
未经授权禁止转载,违者必究!