xgqfrms™, xgqfrms® : xgqfrms's offical website of cnblogs! xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!

如何使用 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/Reference/Global_Objects/Promise/then#return_value

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...');
  });

image

https://stackoverflow.com/questions/71562261/error-message-uncaught-in-promise-but-i-cant-find-whats-wrong-with-that-af

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://stackoverflow.com/questions/71562261/error-message-uncaught-in-promise-but-i-cant-find-whats-wrong-with-that-af

https://stackoverflow.com/questions/23803743/what-is-the-explicit-promise-construction-antipattern-and-how-do-i-avoid-it

https://javascript.info/async

(🐞 反爬虫测试!打击盗版⚠️)如果你看到这个信息, 说明这是一篇剽窃的文章,请访问 https://www.cnblogs.com/xgqfrms/ 查看原创文章!



©xgqfrms 2012-2021

www.cnblogs.com/xgqfrms 发布文章使用:只允许注册用户才可以访问!

原创文章,版权所有©️xgqfrms, 禁止转载 🈲️,侵权必究⚠️!


posted @ 2020-11-21 17:20  xgqfrms  阅读(1130)  评论(8编辑  收藏  举报