簡體   English   中英

計時器包裝函數,用於遞歸異步/等待調用

[英]timer wrapper function for recursive async/await call

我有一個遞歸異步函數getResponse(url,attempts = 0) ,該函數輪詢外部api的響應並在達到X次重試次數或服務器錯誤后解析或退出。 但是,它的內部“時鍾”基於重試的次數(允許延遲以避免速率限制后),但是我還希望在設置基於時間的計時器方面具有靈活性,這將解決該功能並結束遞歸。 理想情況下,我希望能夠將基於時間的計時器包裝在我的遞歸異步函數周圍,例如timed(getResponse(url),3400)

我僅通過將兩個計時器打包到一個異步函數中並將本地變量expired作為退出標志,並在兩個函數上設置Promise.race條件,使基於時間的計時器和基於“重試”的計時器一起工作。

async function timedgetResponse (expiry = 3500,url) {
  let expired = false;
  async function timeout(expiry){
    await new Promise(_=> setTimeout(_,expiry));
    expired = true;
    return false;
  };

async function getResponse(url,attempts = 0){
  try {
    if(expired){ return false; };
    const limit = 10;
    if(attempts >= limit){ok: false, e:"MAX_ATTEMPTS"};
    const rawRes = await fetch(url,
      {
        method: 'GET',
        credentials: 'include', 
        headers: {
          'Accept': 'application/json'
        }
      });
    if (!rawRes.ok) {  throw (Error('SERVER_ERROR')); }; 
    const res = await rawRes.json(); 
    if(!res || res.status === 0){ throw (Error(res.request)); };
    return {ok: true, res: res.request};
  } catch(e){
  const err = e.message; 
  if(err === "RESPONSE_NOT_READY"){
    await new Promise(_ => setTimeout(_, 333));
    attempts +=1;   
    return getResponse(url,attempts);
  } else 
  if(err === "SERVER_ERROR_ON_RESOLVER"){
    await new Promise(_ => setTimeout(_, 10000));
    attempts +=1;       
    return getResponse(url,attempts);
  } else {
    return {ok: false, e:"MISC_ERROR"};
  };

};    
};

  const awaited = await Promise.race([
    getResponse(url),
    timeout(expiry)
  ]);

return awaited;
};

我認為這不是正確的方法,不勝感激對timed(getResponse(url),3400)解決方案的任何幫助。

我的功能可能會滿足您的需求。 我已經根據我對您的需求的理解對其進行了更新。 想法是您將輪詢直到某些事情成為事實,即解決或您超過最大嘗試次數。 它具有內置的可配置延遲。

這里的想法是您傳遞一個包裝您的fetch調用的函數,該函數最終將解析/拒絕。

setPolling(pollFunc,freq = 1000,maxAttempts = 3)

pollFunc =不帶參數的函數,返回一個最終解決或拒絕的promise。

freq =運行pollFunc的頻率(以毫秒為單位)

maxAttempts =放棄前的最大嘗試次數

 const setPolling = async (pollFunc, freq = 1000, maxAttempts = 3, _attempts = 1) => { const wait = (delay) => new Promise(resolve=>setTimeout(resolve, delay)) try { return await pollFunc() } catch (e) { if (_attempts < maxAttempts) { await wait(freq) return await setPolling(pollFunc, freq, maxAttempts, ++_attempts) } throw (e instanceof Error) ? e : new Error((typeof e !== 'undefined') ? e : 'setPolling maxAttempts exceeded!') } } async function alwaysFail() { throw new Error(`alwaysFail, failed because that's what it does!`) } function passAfter(x) { let i = 0 return async ()=> { if (i > x) return `passAfter succeeded because i(${i}) > x(${x})` throw new Error(`i(${i++}) < x(${x})`) } } setPolling(alwaysFail) .catch((e)=>console.error(`alwaysFail, failed!\\n${e.message}\\n${e.stack}`)) setPolling(passAfter(5), 500, 10) .then((res)=>console.log(`passAfter, succeeded!\\n${res}`)) .catch((e)=>console.error(`passAfter, failed!\\n${e.message}\\n${e.stack}`)) 

基於要在計時器到期時停止重試的基礎,則可以使用token將停止信號傳遞給遞歸過程。

這樣的事情應該做到:

const poll = async (work, options, token) => {
    const settings = Object.assign({ 'delay':0, 'attempts':1, 'maxAttempts':3 }, options);

    // Utility function which returns a Promise-wrapped setTimeout
    const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));

    // Two mechanisms for stopping the recursion.
    // Performing the tests here ensures they are applied before the first work() call.
    // 1. token-borne stop signal 
    if(token.stop) {
        throw new Error('poll(): stopped');
    }
    // 2. max attempts reached
    if (settings.attempts >= settings.maxAttempts) {
        throw new Error('poll(): max attempts reached');
    }

    // Do the work, and recurse on error
    try {
        return await work();
    }
    catch (e) {
        await delay(settings.delay);
        settings.attempts += 1; // Mutate/pass `settings`; the original `options` is not guaranteed to have an `attempts` property.
        return await poll(work, settings, token);
    }
}

調用如下:

// token
const token = {}; // or {'stop':false} if you like

// Time based timer:
setTimeout(() => {
    token.stop = true; // raise the 'stop' flag
}, 60000); // or whatever

let pollPromise = poll(doSomethingAsync, {'delay':1000, 'maxAttempts':100}, token)
.then((res) => console.log(res))
.catch((e) => console.error(e));

請注意,在設置停止信號時:

  1. 機上工作仍將獲得成功的回應。
  2. 進一步的遞歸將被阻止,但不會嘗試中止正在進行的工作。

再多考慮一下,可以根據實際需要更改這些行為。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM