Daily-Interview-Question icon indicating copy to clipboard operation
Daily-Interview-Question copied to clipboard

第 159 题:实现 Promise.retry,成功后 resolve 结果,失败后重试,尝试超过一定次数才真正的 reject

Open yygmind opened this issue 4 years ago • 69 comments

yygmind avatar May 21 '20 01:05 yygmind

Promise.retry = function (promiseFn, times = 3) {
  return new Promise(async (resolve, reject) => {
    while (times--) {
      try {
        var ret = await promiseFn();
        resolve(ret);
        break;
      } catch (error) {
        if (!times) reject(error);
      }
    }
  });
};
function getProm() {
    const n = Math.random();
    return new Promise((resolve, reject) => {
        setTimeout(() =>  n > 0.9 ? resolve(n) : reject(n), 1000);
    });
}
Promise.retry(getProm);

bighamD avatar May 21 '20 03:05 bighamD

image

liujiekun avatar May 21 '20 04:05 liujiekun

image

FunnyBoyCN avatar May 21 '20 05:05 FunnyBoyCN

image

ShinningFire avatar May 21 '20 07:05 ShinningFire

写的不好,有好的写法大家可以指点一下,感谢

/*
          实现 Promise.retry,成功后 resolve 结果,失败后重试,尝试超过一定次数才真正的 reject
        */

      // 同步获得Promise状态和值,这样写很low,要是更好的写法大家call我一下
      async function checkPromise(getFlag) {
        try {
          let res = await getFlag();
          return { flag: true, value: res };
        } catch (e) {
          return { flag: false, value: e };
        }
      }

      async function retry(pro, count = 10) {
        let result = {
            flag: false,
            value: null,
          },
          length = count;
        while (count && !result.flag) {
          console.log(`调用了${length - count + 1}次`);
          result = await checkPromise(pro);
          const { flag, value } = result;
          if (!flag) {
            count--;
          }
        }
        return result.value;
      }
      Promise.retry = retry;

      getFlag = function () {
        return Math.random() > 0.5 ? Promise.resolve(1) : Promise.reject(-1);
      };

      Promise.retry(getFlag, 10)
        .then((res) => {
          console.log("成功结果", res);
        })
        .catch((e) => {
          console.log("失败结果", e);
        });

weiweixuan avatar May 21 '20 11:05 weiweixuan

 Promise.retry = function(fn, num){
    return new Promise(function(resolve, reject){
       while(num>0){
           try{
                  const res = await fn
                  resolve(res)
                  num = 0
            } catch(e){
                  if(!num) reject(e)
            }
            num --
        }
    })
}

fanefanes avatar May 21 '20 13:05 fanefanes

function retry (fn, count = 10) {
  return new Promise(async (resolve, reject) => {
    while (count) {
      try {
        let res = await fn()
        resolve(res)
        return
      } catch (e) {
        if (!count) reject(e)
        count--
      }
    }
  })
}

listentolife avatar May 24 '20 07:05 listentolife

/** 
 * 实现 Promise.retry,成功后 resolve 结果,失败后重试,尝试超过一定次数才真正的 reject 
*/
Promise.retry = function(fn, limit) {
  let i = 0
  return new Promise((resolve, reject) => {
    function retry() {
      console.log('try!', i)
      fn().then(res => {
        resolve()
      }).catch(err => {
        i++
        if(i > limit - 1) {
          reject(err)
        } else {
          retry()
        }
      })
    }
    retry()
  })
  
}
Promise.retry(() => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      Math.random() > .5 ? resolve() : reject()
    }, 300);
  })
}, 5).then(res => {
  console.log('resolve')
}).catch(err => {
  console.log('reject')
})

rottenpen avatar May 25 '20 15:05 rottenpen

image

huangzhiyang166 avatar May 27 '20 02:05 huangzhiyang166

方法一:在 catch() 中再次执行函数

Promise.retry = function (fn, times) {
  fn().then(console.log)
    .catch(e => {
      if (times > 0) {
        console.log('try again...')
        Promise.retry(fn, times - 1)
      } else {
        console.log('Error: No more times, now rejected.')
      }
    })
}

方法二:async 函数,结合 while 循环和 try...catch

Promise.retry = async function (fn, times) {
  while (times > 0) {
    try {
      const res = await fn()
      console.log(res)
      return
    } catch(e) {
      console.log('try again...')
      times--
    }
  }
  console.log('Error: No more times, now rejected.')
}

测试:

const test = function () {
  return new Promise((resolve, reject) => {
    const num = Math.floor(Math.random() * 10)
    if (num > 7) {
      resolve(num)
    } else {
      reject(new Error(num))
    }
  })
}

Promise.retry(test, 5)

ghost avatar May 27 '20 06:05 ghost

记得几年前面试一次面试时问过类似的问题,当时绞尽脑汁也没想出解决办法



/**
 * promiseFn 返回promise 对象的函数
 * max       最大重试次数
 * interval  重试间隔时间
*/
Promise.retry = function (promiseFn, max=1, interval = 1000) {
    function excuteFn(max) {
        return promiseFn().then(res => {
            return Promise.resolve(res);
        }, err => {
            if (max <= 1) {
                return Promise.reject(err);
            } else {
                return new Promise((resolve, reject) => {
                    setTimeout(() => {
                        resolve(excuteFn(--max));
                    }, interval);
                })
            }
        })
    }
    return excuteFn(max)
}


// 测试用例
Promise.retry(ajax, 5, 1000)
    .then(res => console.log('retry-res->', res))
    .catch(err => console.log('retry-err->', err))



function ajax() {
    const n = Math.random();
    console.log('--->', n)
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            if (n > 0.9) {
                resolve(`成功返回值--->${n}`)
            } else {
                reject(`失败返回值--->${n}`)
            }
        }, 3000);
    })
}

hugeorange avatar May 28 '20 09:05 hugeorange

// 实现 Promise.retry,成功后 resolve 结果,失败后重试,尝试超过一定次数才真正的 reject 
Promise.retry = function(fn, num){
  return new Promise(async function(resolve, reject){
     while(num>0){
         try{
            const res = await fn();
            console.log('res: ', res);
            resolve(res)
            num = 0
          } catch(e){
            console.log('reject....');
            if(num === 1) reject(e);
            num--
          }
      }
  })
}

const test = () => {
  return new Promise((resolve, reject) => {
    const num = Math.floor(Math.random() * 10);
    if (num > 5) {
      resolve(num);
    } else {
      reject(`num 小于等于8`);
    }
  
  })
}

Promise
.retry(test, 5)
.then(res => {
  console.log('result: ', res);
})
.catch(err => {
  console.log('err: ', err);
})

dice246 avatar May 29 '20 02:05 dice246

更加具有抽象形式的retry,尝试反复执行一个方法,方法内部可以是异步请求,或者其他需要retry的动作,如请求动画等等。所以,我给出的retry函数的ts形式是这样的。

function retry(times:number,fn:Function){
    let counter = 0,maxTimes = times +1;
    let resp , err;
    while(counter < maxTimes){
           err = undefined;
           try{
                resp = fn();
           }catch(error){
                err = error;
           }
           if(err === undefined){
                 break;
           }
    }
    if(err){
       throw err;
    }

    return resp;
}

retry(3,()=>{
  console.log("test");
  throw new Error("retry error")
  return {}
})

lvanboy avatar Jun 01 '20 02:06 lvanboy

// 实现 Promise.retry,成功后 resolve 结果,失败后重试,尝试超过一定次数才真正的 reject
// 这道题刚开始做的时候,写漏了一个条件:超过一定次数才是真正的reject。
// 这里用for + break 去做次数限制
Promise.retry = function(fn, max = 5) {
    return new Promise(async (resolve, reject) => {
        let res = null
        for (let i in [...Array(max)]) {
            try {
               console.log('try: ', i)
               res = await fn()
               break
            } catch(e) {
                if(+i === (max - 1)) {
                    reject(e)
                    return
                }
            }
        }
        resolve(res)
    })
}

function fn () {
    let n = Math.random()
    return new Promise((resolve, reject) => {
        setTimeout(() => {
           console.log('Current random value: ', n)
           n > 0.75 ? resolve(n) : reject(n)
        }, 2000)
    })
}

Promise.retry(fn, 10)
.then(res => console.log('Resolve: ', res))
.catch(res => console.log('Reject: ', res))

MengYiFan avatar Jun 02 '20 06:06 MengYiFan

感觉题目可以清晰一些,指定retry的入参是啥

 Promise.retry = function (fn, times) {
   return new Promise((rs, rj) => {
      return fn().then((res) => {
        rs(res)
      }).catch((err) => {
        if (--times >= 0) {
          return Promise.retry(fn, times)
        } else {
          rj(err)
        }
      })
   })
 }

qianlongo avatar Jun 03 '20 13:06 qianlongo

为啥感觉你们写的怎么这么复杂 。。。

Promise.retry = function (promiseFunc, num = 2) {
  return promiseFunc().then(null, (e) => {
    if (num > 0) {
      num -= 1;
      console.log("重试");
      return Promise.retry(promiseFunc, num);
    }
    return Promise.reject(e);
  });
};

再简化一下

Promise.retry = function (promiseFunc, num = 2) {
  return promiseFunc().then(null, (e) => num > 0 ? Promise.retry(promiseFunc, num - 1) : Promise.reject(e));
};

ZangYuSong avatar Jun 08 '20 03:06 ZangYuSong

感觉题目可以清晰一些,指定retry的入参是啥

 Promise.retry = function (fn, times) {
   return new Promise((rs, rj) => {
      return fn().then((res) => {
        rs(res)
      }).catch((err) => {
        if (--times >= 0) {
          return Promise.retry(fn, times)
        } else {
          rj(err)
        }
      })
   })
 }

我就想问这一句:return fn().then((res) => rs(res)); 题目没说fn函数返回的一定是Promise吧?普通函数调用then不就报错了

sfsoul avatar Jun 11 '20 12:06 sfsoul



(function () {

  function retry(func, max = 2, err) {
    return new Promise((resolve, reject) => {
      return func()
        .then(resolve)
        .catch(err => {
          console.log('retry', max);
          if (max <= 0) {
            return reject(err)
          }
          return retry(func, max - 1);
        })
        .catch(reject);
      
    })
  }


  retry(() => Promise.resolve(1))
    .then(res => {
      console.log('成功', 1)
    })
    .catch(err => {
      console.log('fail');
    })

  retry(() => {
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        reject(111);
      }, 3000);
    })
  })
    .then(res => {
      console.log('成功', res)
    })
    .catch(err => {
      console.log('fail', err);
    })



})();

GuoYiJi avatar Jun 12 '20 09:06 GuoYiJi

if (Promise) {
  Promise.retry = function (fn, time) {
    return new Promise((resolve, reject) => {
      const retry = (i) => {
        return Promise.resolve(fn()).then(res => {
          resolve(res);
        }).catch(err => {
          if (i <= time) {
            return retry(++i);
          } else {
            reject(err);
          }
        })
      }
      return retry(0);
    });
  }
}

Promise.retry(function () {
 return new Promise((resolve, reject) => {
  setTimeout(() => {
    // resolve('resolve...');
    reject('reject...');
  }, 300);
 });
}, 3).then(res => {
  console.log('res: ', res);
}).catch(err => {
  console.log('err:', err);
});

jefferyE avatar Jun 15 '20 11:06 jefferyE

Promise.retry = function (task, retryTimes) {
        if(retryTimes <= 0) {
          new Error('wrong params that retry times')
        }
        return new Promise((resolve, reject) => {
          function runTask() {
            if(retryTimes === 0) {
              reject(new Error('have no retry times'))
              return
            }
            task()
              .then((v) => {
                resolve(`success ${v}`);
              })
              .catch(() => {
                retryTimes--
                runTask()
              });
          }
          runTask();
        });
      };

NathanHan1 avatar Jun 22 '20 03:06 NathanHan1

Promise.retry = function (getPromise, num) {
  if (num === 1) return getPromise()
  return getPromise().then(res => res, () => Promise.retry(getPromise, --num))
}

leirensheng avatar Jun 26 '20 00:06 leirensheng


Promise.retry = function(fn,max = 3){
	let count = 0;
	return new Promise((resolve,reject)=>{
		 function handler(){
			fn().then((res)=>{
				resolve(res)
			}).catch((err)=>{
				count++;
				if(count >= max){
					reject(err)
				}else{
					handler()
				}
			})
		}

		handler()
	})
}

xuj2017 avatar Jun 30 '20 08:06 xuj2017

Promise.retry = (times = 10, fn) =>
  // eslint-disable-next-line no-async-promise-executor
  new Promise(async (resolve, reject) => {
    while (times > 0) {
      // eslint-disable-next-line no-param-reassign
      times -= 1;
      try {
        // eslint-disable-next-line no-await-in-loop
        const ret = await fn();
        return resolve(ret);
      } catch (err) {
        if (times <= 0) {
          return reject(err);
        }
      }
    }
  });

// Test case
const test = () => new Promise((resolve, reject) => {
  if (Math.random() < 0.2) {
    resolve('good luck');
  } else {
    reject(new Error('bad luck'));
  }
});


Promise.retry(2, test).then((ret) => {
  console.log(ret);
}).catch((err) => {
  console.log(err);
});

yuanyu4 avatar Jul 03 '20 05:07 yuanyu4

async function retry<T>(n: number, action: () => Promise<T>): Promise<T> {
  if (n <= 0) throw "Try all"
  try {
    let result = await action()
    return result
  } catch(_) {
    return retry(n - 1, action)
  }
}

ustchcl avatar Jul 05 '20 15:07 ustchcl

比如重试 5 次

let time = () => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      reject("error");
    }, 1000);
  });
};

async function f(retry = 5) {
  while (retry !== 0) {
    try {
      const result = await time();
      return result;
    } catch (e) {
      if (--retry === 0) {
        throw e
      }
    }
  }
}

zhangwinning avatar Jul 07 '20 01:07 zhangwinning

//函数
Promise.retry = function (promise, num) {
    console.log(num);
    return promise().then(res => res, function () {
        return num == 1 ? promise() : Promise.retry(promise, --num)
    });
}
//箭头函数
Promise.retry = (promise, num) =>
    promise().then(res => res, () =>
        num == 1 ? promise() : Promise.retry(promise, --num)
    );


//test
Promise.retry(Promise.resolve.bind(Promise, "reslove"), 5);
Promise.retry(Promise.reject.bind(Promise, "reject"), 5);

darkestinblack avatar Jul 16 '20 02:07 darkestinblack

实现

对于常见的应用场景,至少应该满足重试次数和每次重试的间隔可配置,失败后立即重试意义并不大。

/**
 * Promise.retry,成功后 resolve 结果,失败后重试,尝试超过一定次数才真正的 rejec
 * @param {(resolve, reject) => any} fn 执行函数
 * @param {number} max 最大尝试次数
 * @param {number} interval 尝试间隔 
 * @param {number} count 函数内部统计 
 */
export function retry(fn, max = 1, interval = 1000, count = 0) {
  --max
  ++count
  return new Promise((resolve, reject) => {
    const wrapResolve = (res) => {
      resolve(res)
    }
    const wrapReject = (error) => {
      if (max >= 0) {
        setTimeout(() => {
          console.log(`第${count}次重试`)
          resolve(retry(fn, max, interval, count))
        }, interval)
      } else {
        reject(error)
      }
    }
    fn(wrapResolve, wrapReject)
  })
}

Promise.retry = retry

测试

// 单纯的失败场景
function testFail(_, reject) {
  reject(new Error('error'))
}
Promise.retry(testFail, 10)
  .catch(error => conso.log(error)
// 单纯的成功场景
function testSuccess(resolve) {
  resolve('success')
}
Promise.retry(testSuccess, 10)
  .then(res => console.log(res))
// 重试成功的场景
let count = 0
function testRetrySuccess(resolve, reject) {
  ++count
  count > 3 ? resolve('success') : reject(new Error('error'))
}
Promise.retry(testRetrySuccess, 10)
  .then(res => console.log(res))

wenchao0809 avatar Aug 01 '20 13:08 wenchao0809

Promise.retry = function(fn,time=5){
  let flag = false
  return new Promise(async (resolve,reject)=>{
     while(time>0 && !flag){
	try{
	    const res = await fn()
	    resolve(res)
	   flag = true
	  }catch(e){
	      if(time<=0){
		 reject(e)
	      }
	    time--
	  }
       }
   })
}

markgzl avatar Aug 06 '20 08:08 markgzl

实现

含最大次数 & 延迟执行

Promise.retry = (fn, options = {}) => {
  const { max = 3, delay = 0 } = options
  let curMax = max
  const delayExec = () => delay && new Promise(resolve => setTimeout(resolve, delay))

  return new Promise(async (resolve, reject) => {
    do {
      try {
        const res = await fn()
        resolve(res)
        return
      } catch (error) {
        await delayExec()
        curMax--
        console.warn(`剩余次数${curMax}`)
        if (!curMax) reject(error)
      }
    } while (curMax > 0)
  })
}


测试

const resolveData = () => {
  return new Promise((resolve, reject) => {
    setTimeout(
      () => (Math.random() > 0.5 ? resolve('成功') : reject(new Error('失败')))
      , 1000,
    )
  })
}

;(async () => {
  try {
    const res = await Promise.retry(resolveData, { delay: 1000 })
    console.warn('result', res)
  } catch (error) {
    console.warn(error)
  }
})()

zhixiaoqiang avatar Aug 07 '20 16:08 zhixiaoqiang

Promise.retry = function(fun,max=2){
      return fun().then(Promise.resolve(),(err)=>{
        if(!max){
          return Promise.reject(err);
        }else{
          return Promise.retry(fun,--max);
        }
      })
    }
    function getProm() {
      const n = Math.random();
      return new Promise((resolve, reject) => {
          setTimeout(() =>  n > 0.5 ? resolve(n) : reject(n));
      }, 1000);
    }

    Promise.retry(getProm).then((a)=>{
      console.log(a);
    });

qimiaowan avatar Aug 17 '20 08:08 qimiaowan

Promise.retry=function(fn,number=3){
    return function(...args){
        let time=0;
        var next=function(resolve,reject){
            fn(...args)
            .then(function(...res){
                console.log(res)
                resolve(...res)
            })
            .catch(function(...error){
                if(time===number){
                    reject(...error);
                    time=0;
                }else{
                    time++;
                    next(resolve,reject)
                }
            })
        }
        return new Promise(function(resolve,reject){
           next(resolve,reject)
        })
    }
}


function testSuccess(params) {
    return new Promise(function(resolve,reject) {
        resolve(params)   
    })
}
function tetestError(params) {
    return new Promise(function(resolve,reject) {
        reject(params)   
    })
}

let success=Promise.retry(testSuccess);
success('我是成功')
.then(function(params) {
    console.log(params)
})
.catch(function (err) {
    console.log(err)
})

let error=Promise.retry(tetestError);
error('我是成功')
.then(function(params) {
    console.log(params)
})
.catch(function (err) {
    console.log(err)
})

webzsp avatar Aug 24 '20 03:08 webzsp

Promise.prototype.retry = function(fn) {
  var resolve;
  var reject;
  
  var p = new Promise(function(res, rej) {
    resolve = res;
    reject = rej;
  });

  var retryReject = function(err) {
    fn(resolve, reject)
  }

  fn(resolve, retryReject);
  return p;
}

Promise.prototype.retry((resolve, reject) => {
  console.log('尝试')
  reject('err')
}).then(res => {console.log(res)}, rej => {
  console.log(rej);
})

carbrokers avatar Aug 29 '20 06:08 carbrokers

Promise.retry = function(fn, times) {
  let n = 0
  return new Promise((resolve, reject) => {
    next(resolve, reject)
  })
  function next(resolve, reject) {
    fn().then(resolve).catch(e => {
      n++
      n === times ? reject(e) : next(resolve, reject)
    })
  }
}

wabRul avatar Sep 02 '20 07:09 wabRul

上面大部分有有个问题,不一定try-catch报错了才reject,得到不符合预期的结果也是reject的

cool-518 avatar Sep 05 '20 09:09 cool-518

Promise.retry = (promise, times) => {
  return new Promise((resolve, reject) => {
    const fn = async (promise, num = 0) => {
      try {
        const res = await promise;
        resolve(res);
      } catch (error) {
        if (num >= times) {
          reject(error);
        } else {
          fn(promise, num + 1);
        }
      }
    };
    fn(promise);
  });
};

zjjjjjjjjjjd avatar Sep 06 '20 06:09 zjjjjjjjjjjd

Promise.retry = async (fn, times) => {
  while (true) {
    try {
      return await fn()
    } catch (e) {
      if (times-- === 0) {
        throw e
      }
    }
  }
}

halionn avatar Sep 18 '20 02:09 halionn

// async/await
Promise.retry = (asyncFn, times) => {
    // 把重试函数包装成 Promise
  let retPromsie = () => Promise.resolve().then(() => {
    try {
      return asyncFn()
    } catch(e) {
      throw Error(e)
    }
  })

  return new Promise((resolve, reject) => {
    let i=times
    const retry = async () => {
      if(i==0) return
      
      try {
        let ret = await retPromsie()
        ret!=undefined && resolve(ret)  
      } catch (e) {
        i==1 && reject(e)
      }

      i--
      retry()
    }
    retry()
  })
}
// const fn = () => a+b // ReferenceError: a is not defined
const fn = (a, b) => a+b // 3
Promise.retry(fn.bind(null, 1,2), 3).then(console.log, console.log)

// Promise
Promise.retry = (asyncFn, times) => {
  // 把重试函数包装成 Promise
  let retPromsie = () => Promise.resolve().then(() => {
    try {
      return asyncFn()
    } catch(e) {
      throw Error(e)
    }
  })
  const promiseFn = asyncFn instanceof Promise ? asyncFn : retPromsie

  if(times==1) return Promise.reject(promiseFn())
  
  return Promise.resolve(promiseFn()).then(null, () => Promise.retry(promiseFn, times-1))
}

// const fn = () => a+b // ReferenceError: a is not defined
const fn = (a, b) => a+b // 3
Promise.retry(fn.bind(null, 1,2), 3).then(console.log, console.log)

m7yue avatar Sep 24 '20 09:09 m7yue

Promise.retry = function (fn, max = 3) {
  let p = new Promise(fn);
  while (max) {
    p = p.catch(() => {
      console.log("失败");
      return new Promise(fn);
    });
    max -= 1;
  }
  return p;
};

// 测试  执行一个递增的i;i>1时会成功
let i = 0;
Promise.retry((resolve, reject) => {
  setTimeout(() => {
    if (i > 1) resolve("我成功了");
    i += 1;
    reject();
  }, 1000);
}).then(
  (res) => {
    console.log("res", res);
  },
  (err) => {
    console.log("err", err);
  }
);

z253573760 avatar Oct 02 '20 18:10 z253573760

var num = 0

var fn = () => {
    num++
    return new Promise((resolve, reject) => {
        if (num == 5) { // 修改这个数字可以模拟失败多少次
            resolve('成功')
        } else {
            reject('失败')
        }
    })
}

Promise.retry = function (promiseFn, times, interval) {
    return new Promise((resolve, reject) => {
        let recur = () => {
            promiseFn().then((data) => {
                resolve(data)
            }).catch(e => {
                times--
                console.log('失败了一次');
                if (times === 0) {
                    reject(e)
                } else {
                    setTimeout(() => {
                        recur()
                    }, interval);
                }
            })
        }
        recur()
    })
}

Promise.retry(fn, 3, 2000).then((data) => {
    console.log(data);
},(e) => {
    console.log(e);
})

sakz avatar Oct 05 '20 20:10 sakz

function retry (fn, count = 10) {
  return new Promise(async (resolve, reject) => {
    while (count) {
      try {
        let res = await fn()
        resolve(res)
        return
      } catch (e) {
        if (!count) reject(e)
        count--
      }
    }
  })
}

应该先count--,不然永远也进不了catch那步啊

(() => {
    Promise._retry = function (fn, count) {
        return new Promise(async (resolve, reject) => {
            while (count) {
                try {
                    const res = await fn()
                    resolve(res)
                    return
                } catch (err) {
                    count--;
                    if(count) console.log('重试中,剩余次数:' + count); // 用例
                    if (!count) reject(err)
                }
            }
        });
    }

    // 用例
    let p = function () {
        return new Promise((resolve, reject) => {
            reject('没有了,没有总冠军了o(╯□╰)o');
        });
    };
    Promise._retry(p,10).then(res=>console.log(res)).catch(e=>console.log(e))
})();

Mrcxt avatar Oct 29 '20 07:10 Mrcxt

弱弱的发一个, 思路: fn 属于 Promise while 需要 await 阻塞 递归 可以直接回调 异步

Promise.retry = function (fn, count) {
  return new Promise((resolve, reject) => {
    console.log(count)
    const pro = fn();
    if (!(pro instanceof Promise)) {
      i = 0;
      throw new Error('不是 Promise');
    }
    pro.then((res) => {
      resolve(res)
    }).catch(e => {
      if (count <= 0) {
        return reject(e);
      }
      Promise.retry(fn, count--);
    })
  })
}

hcc0007 avatar Nov 05 '20 16:11 hcc0007

promise里用try cath的你们不觉得诡异的么🙄,好好的在其promise上链式catch不好吗的。。。

Promise.retry = function (promiseFn, limitTime = 3) {
    function executeFn(count = 1) {
        console.count('执行了');
        return promiseFn().then(res => res).catch(err => {
            if (count < limitTime) {
                return executeFn(count + 1);
            } else {
                return Promise.reject(err);
            }
        });
    }
    return executeFn();
};
// 测试用例
function test(time = 5) {
    let count = 0;

    function res() {
        count++;
        if (count < time) {
            return Promise.reject('no');
        } else {
            return Promise.resolve('yes'); //默认第time次执行才成功
        }
    }
    return res;
}
const testFn = test();

const restryTime = 3; //重试次数
// const restryTime = 5;
Promise.retry(testFn, restryTime).then(res => {
    console.log(res)
}).catch(err => {
    console.log(err)
})

JianJroh avatar Nov 24 '20 04:11 JianJroh

实现

Promise.retry = (fn = () => {}, times = 0) =>
  new Promise(async (resolve, reject) => {
    let count = -1;
    retryTask: 
    while (count < times) {
      try {
        (count !== -1) && console.log(`retry No.${count + 1}`);
        const res = await fn();
        resolve(res);
        break retryTask;
      } catch (err) {
        (++count === times) && reject(err);
      }
    }
  });

测试

// test function
const task = () => {
  return new Promise((resolve, reject) => {
    console.log('task run');
    setTimeout(() => {
        reject(new Error());
    }, 1000);
  })
};
// case1
Promise.retry(task).then(res => {
  console.log('Task1 resolved');
}).catch(err => {
  console.error('Task1 rejected, error is:', err);
})

image

// case2
Promise.retry(task, 1).then(res => {
  console.log('Task2 resolved');
}).catch(err => {
  console.error('Task2 rejected, error is:', err);
})

image

// case3
Promise.retry(task, 6).then(res => {
  console.log('Task3 resolved');
}).catch(err => {
  console.error('Task3 rejected, error is:', err);
})

image

ReAlign avatar Nov 24 '20 12:11 ReAlign

var count = 1;

	function ajax() {
		return new Promise((resolve, reject) => {
			setTimeout(() => {
				if (count > 4) {
					resolve('success')
				} else {
					count++;
					reject('error')
				}
			}, 1000)
		})
	}

	Promise.retry = function (promise, num) {
		let c = 1;
		return new Promise((resolve, reject) => {
			function loop() {
				promise().then(res => {
					resolve(res)
				}).catch(err => {
					if (c > num) {
						reject(err)
					} else {
						c++
						loop();
					}
				})
			}

			loop()
		})

	}
	Promise.retry(ajax, 8).then(res => {
		console.log(res)
	}).catch(err => {
		console.log(err)
	})

xiaowuge007 avatar Jan 20 '21 09:01 xiaowuge007

function retry2(fn, times) {
	times--;
	return new Promise((resolve, reject) => {
		resolve(fn());
	})
		.then((res) => {
			return times;
		})
		.catch((err) => {
			if (times > 0) {
				return retry2(fn, times);
			}
			return Promise.reject(err);
		});
}

Luz-Liu avatar Feb 20 '21 02:02 Luz-Liu

/**
  * 
  * @param {function} fn 请求后端接口的方法
  * @param {number}   times 重试次数
  * @param {function} retryCondition 触发重试的条件
  * @returns {function}
  */
 function withRetry(fn,times=3,retryCondition){
	return function(){
		const args = Array.prototype.slice.call(arguments);
		return new Promise((resolve,reject) => {
			doRequest(resolve,reject);
			function doRequest(resolve,reject){
				fn.apply(null,args).then((res) => {
					if(!retryCondition(res)) return resolve(res);
					if(times==0) return reject(res);
					times--;
					doRequest(resolve,reject);
				}).catch(e=>{
					if(times==0) return reject(e);
					times--;
					doRequest(resolve,reject);
				})
			}
		})
	}
}

huangzhiyang166 avatar Mar 19 '21 09:03 huangzhiyang166

  • 如果可以用 async await 的话,那么可以用 while 循环,通过同步的语法实现
  • 如果不能用 async await 的话,那么可以用 Promise 递归调用的方法实现

如果要求两次请求间隔一段时间,用 promise 会更方便

在外层使用 async

Promise.retry = async function (fn, times = 3) {
  while (true) {
    console.log(times);
    times--;
    try {
      const res = await fn();
      return res;
    } catch (error) {
      if (times === 0) throw error;
    }
  }
};

在内层使用 async

Promise.retry = function (promiseFn, times = 3) {
  return new Promise(async (resolve, reject) => {
    while (true) {
      console.log(times);
      times--;
      try {
        var ret = await promiseFn();
        resolve(ret);
        break;
      } catch (error) {
        if (times === 0) {
          reject(error);
          break;
        }
      }
    }
  });
};

使用 promise

Promise.retry = function (fn, times = 3) {
  return new Promise(function (resolve, reject) {
    function retry() {
      console.log(times);
      times--;
      fn()
        .then(resolve)
        .catch((error) => {
          if (times === 0) {
            reject(error);
          } else {
            retry();
          }
        });
    }
    retry();
  });
};

jasonzhouu avatar Apr 10 '21 03:04 jasonzhouu

 Promise.retry = function (callback) {
      return new this(callback)
        .then((res) => {
          return res;
        })
        .catch(() => {
          return new this(callback);
        });
    };

 Promise.retry((resolve, reject) => {
      setTimeout(() => {
        const nub = Math.random();
        console.log(nub);
        nub > 0.5 ? resolve(nub) : reject(nub);
      }, 1000);
    }).then(
      (res) => {
        console.log("res", res);
      },
      (err) => {
        throw err;
      },

haipingzi avatar Apr 26 '21 01:04 haipingzi

 Promise.retry = function (callback, count = 1) {
      const retryHandle = () => {
        return new this(callback)
          .then((res) => {
            return res;
          })
          .catch((error) => {
            count--;
            console.log(count);
            if (count <= 0) {
              return this.reject(error);
            } else {
              return retryHandle();
            }
          });
      };
      return retryHandle();
    };

    Promise.retry((resolve, reject) => {
      setTimeout(() => {
        const nub = Math.random();
        console.log(nub);
        nub > 0.5 ? resolve(nub) : reject(nub);
      }, 1000);
    }, 5).then(
      (res) => {
        console.log("res", res);
      },
      (err) => {
        throw err;
      },
    );

haipingzi avatar Apr 26 '21 02:04 haipingzi

image

chengruixin avatar Jun 09 '21 08:06 chengruixin

使用生成器实现

Promise.retry = function (promiseFunc, times = 3) {
  return new Promise((resolve, reject) => {
    function * gen () {
      let ret = yield promiseFunc()
      while (times --) {
        ret = yield promiseFunc()
      }
      return ret
    }

    const iterator = gen()

    const onFulfilled = (res) => resolve(res)

    const onRejected = (err) => {
      let ret = iterator.next()
      if (ret.done) {
        return reject(err)
      }
      next(ret)
    }

    const next = (ret) => ret.value.then(onFulfilled, onRejected)

    next(iterator.next())
  })
}

WebXiaoMing avatar Jun 30 '21 17:06 WebXiaoMing

Promise.retry = function (promiseFun, times = 5) {
  return new Promise((resolve, reject) => {
    const getResult = () => {
      times --;
      promiseFun().then(res => {
        console.log(res)
        resolve(res);
      }).catch(error => {
        console.log(error)
        if (times <= 0) {
          reject(error);
        } else {
          getResult();
        }
      });
    }
    getResult();
  })
}
Promise.retry(async () => {
  const randomNum = Math.random() - 0.9;
  if (randomNum > 0) {
    return `success: ${randomNum}`;
  } else {
    return Promise.reject(`fail: ${randomNum}`)
  }
})

poppydani avatar Jul 09 '21 06:07 poppydani

直接递归处理

Promise.retry = function (promiseFn, times = 3) {
  return new Promise((resolve,reject)=>  promiseFn().then(res=>resolve(res)).catch(err=>times ? Promise.retry(promiseFn, times-1) : reject(err)))
}

dogfeeling avatar Sep 27 '21 08:09 dogfeeling

Solution using recursion

Promise.retry = (func, times = 3) => {
  return new Promise(async (resolve, reject) => {
    try {
      await func();
      resolve();
    } catch(e) {
      times--;
      if (!times) reject();
      else Promise.retry(func, times);
    }
  })
}

const execute = (str) => {
  console.log(str);
  return new Promise((resolve, reject) => {
    setTimeout(() =>  Math.random() > 0.5 ? resolve() : reject(), 500);
  })
}

Promise.retry(execute.bind(null, 'execute'));

W-Ace avatar Feb 15 '22 12:02 W-Ace

你的邮件我已收到,我会马上查看! —————————————————————— 现在的追逐梦想是为了日后能拥有美好的“那些年”!

listentolife avatar Feb 15 '22 12:02 listentolife

Promise.retry = function (promiseFn, times = 3) {
  //success resolve, else try times
  let count = times;
  let promise = promiseFn;

  return new Promise((resolve, reject) => {
    _retry(resolve, reject);

    function _retry(resolve, reject) {
      promise().then(
        (res) => resolve(res),
        (err) => {
          if (count == 0) return;
          
          count--;
          console.log('重新尝试连接');
          _retry(resolve, reject);
        }
      );
    }
  });
};

function getProm() {
  const n = Math.random();
  return new Promise((resolve, reject) =>
    setTimeout(() => (n > 0.9 ? resolve(n) : reject(n)), 1000)
  );
}

try {
  Promise.retry(getProm).then(
    (res) => console.log(res),
    (err) => console.log(err)
  );
} catch (e) {
  console.log(e);
}

Yxgm avatar May 31 '22 11:05 Yxgm

Promise.retry = (promiseFn, tries = 3) => {
  return new Promise(async (resolve, reject) => {
    while (tries > 0) {
      tries--;
      try {
        const result = await promiseFn();
        resolve(result);
        break;
      } catch (error) {
        if (tries === 0) reject(error);
      }
    }
  });
};

const over70 = () => {
  const n = Math.floor(Math.random() * 100);
  return new Promise((resolve, reject) => {
    setTimeout(() => (n > 70 ? resolve(n) : reject(n)), 10);
  });
};

Promise.retry(over70)
  .then((result) => console.log('then', result))
  .catch((result) => console.log('catch', result));

lucaslmlok avatar Jun 20 '22 16:06 lucaslmlok

你的邮件我已收到,我会马上查看! —————————————————————— 现在的追逐梦想是为了日后能拥有美好的“那些年”!

listentolife avatar Jun 20 '22 16:06 listentolife

function retry(fn, options = {}) {
  let { times = 3 } = options;
  const { delay = 1000 } = options;
  return new Promise((resolve, reject) => {
    const tryOnce = () => {
      fn()
        .then(resolve)
        .catch((err) => {
          times -= 1;
          if (times > 0) {
            console.log('retry', times);
            setTimeout(() => {
               tryOnce();
            }, delay);
            return;
          }
          reject(err);
        });
    };
    return tryOnce();
  });
}

imgss avatar Aug 02 '22 08:08 imgss

你的邮件我已收到,我会马上查看! —————————————————————— 现在的追逐梦想是为了日后能拥有美好的“那些年”!

listentolife avatar Aug 02 '22 08:08 listentolife


Promise.retry = function (fn, count = 3) {
    return new Promise((rs, rj) => {
        const p = function () {
            count--;
            return fn().then(r => {
                rs(r);
            }).catch((e) => {
                if (!count) {
                    rj(e);
                } else {
                    p();
                }
            });
        }
        p();
    });
}


function getProm() {
    const n = Math.random();
    return new Promise((resolve, reject) => {
        console.count('getProm');
        setTimeout(() => n > 0.9 ? resolve(n) : reject(n), 1000);
    });
}
Promise.retry(getProm).then(r => {
    console.log('ok')
}).catch(e => {
    console.log('fail')
});

Yangfan2016 avatar Aug 22 '22 13:08 Yangfan2016