轮询直到获得具体结果?

时间:2017-09-13 22:56:41

标签: javascript node.js asynchronous

我目前正在尝试使用此链接https://davidwalsh.name/javascript-polling(和许多其他人)向我的应用添加轮询。

我可以访问以下已经实现的api:

client.get('url')
// returns Promise with result of getting result from url
// for the application I am working on,
//     the URL returns json that looks like the following 
//     {status: DONE or IN PROGRESS, other values...}
// when status is DONE other values are what I will use in the application

client.post('url', {data: passAnyDataHere}) 
// sends a post request with result of sending data to url
// starts the specific job

我遇到的一个问题是,在尝试调整我上面链接的JavaScript轮询代码时,当我发现状态为DONE时,我无法将结果返回到Promise之外。 有人可以给我一些关于如何操作的提示吗?(轮询直到找到特定值,然后返回值以供以后使用)

让我举个例子

export default function someFunction() {
    let a = client.get('/status');
    a.then( dataResult => 
      {
         if (dataResult.status == "DONE") {
            //** want to get other values in dataResult here 
            // and store it somewhere else for use later
         }
      });
    // ***want to work with results here.
    // need some way to get the status of what happened inside the .then(..) part
   //  eventually have to return success or failure and results to the frontend
   // (this part is already done)
 }

代码的基础是https://github.com/erikras/react-redux-universal-hot-example#server-side-data-fetching(使用React.js / Node.js / Redux / etc。)

任何提示/建议/帮助都表示赞赏。谢谢!

此外,我正在处理的应用程序不使用JQuery。

7 个答案:

答案 0 :(得分:3)

Node.js的最新版本支持async / await。

以下是使用它的示例..

async / await的一个主要优点是,很容易遵循代码并理解它的逻辑。例如,如果您想扩展它,以获得最大限度的尝试功能,那么这将是微不足道的。 (提示)它只是一个for循环:)



require 'rubygems'
require 'json'

info = `curl -qs http://localhost:8080/job/#{ENV['JOB_NAME']}/#{ENV['BUILD_NUMBER']}/api/json`

puts JSON.parse(info)['actions'].first['causes'].first['upstreamBuild']




答案 1 :(得分:3)

这是一个使用promises和poll的函数示例,直到获得所需的结果。我还对它进行了参数化,以便您可以传递轮询间隔和超时值:

// create a promise that resolves after a short delay
function delay(t) {
    return new Promise(function(resolve) {
        setTimeout(resolve, t);
    });
}

// interval is how often to poll
// timeout is how long to poll waiting for a result (0 means try forever)
// url is the URL to request
function pollUntilDone(url, interval, timeout) {
    let start = Date.now();
    function run() {
        return client.get(url).then(function(dataResult) {
            if (dataResult.status === "DONE") {
                // we know we're done here, return from here whatever you 
                // want the final resolved value of the promise to be
                return dataResult;
            } else {
                if (timeout !== 0 && Date.now() - start > timeout) {
                    throw new Error("timeout error on pollUntilDone");
                } else {
                    // run again with a short delay
                    return delay(interval).then(run);
                }
            }
        });
    }
    return run();
}

// sample usage
// polls every 500ms for up to 30 seconds
pollUntilDone(someUrl, 500, 30 * 1000).then(function(result) {
   // have final result here 
}).catch(function(err) {
    // handle error here
});

这里的关键是链接你的承诺,所以每当你再次调用run()时,你会返回它的值,因此它被链接到先前的承诺。然后,每当您最终返回一个值或抛出异常时,原始承诺将获得该值或错误作为已解决的值或被拒绝的原因。

请注意,我添加了一个超时,因为你真的永远不想永远进行轮询,特别是在一些无法预料和反复出现的错误可能不会拒绝承诺的情况下,但却无法获得你想要的结果。

答案 2 :(得分:0)

一种选择是将poll功能更改为仅在满足所需条件时解决:

function poll(pollFn, interval = 100) {
    var intervalHandle = null

    return {
        until(conditionFn) {
            return new Promise((resolve, reject) => {
                intervalHandle = setInterval(() => {
                    pollFn().then((data) => {
                        let passesCondition = false;
                        try {
                            passesCondition = conditionFn(data);
                        } catch(e) {
                            reject(e);
                        }
                        if (passesCondition) {
                            resolve(data);
                            clearInterval(intervalHandle);
                        }
                    }).catch(reject)
                }, interval)
            })
        }
    }
}

var counter = 0;

function getStatus() {
    if (counter++ === 5) {
       return Promise.resolve({ status: 'DONE', otherStuff: 'hi' });
    }
    console.log('not DONE, keep going')
    return Promise.resolve({ status: 'WORKING' });
}

poll(getStatus, 500)
  .until(data => data.status === 'DONE')
  .then((data) => {
    // do something with the data
    console.log('status is DONE', data)
  })

答案 3 :(得分:0)

我只需要解决一个类似的问题。 以下是我的解决方案的要点:

// api call with interval until receiving a specific data.

const callApi = () => {
  return new Promise((resolve, reject) => {
    console.log('calledAPI!');
    setTimeout(()=>{
      var myNumber = parseInt(Math.random()*10, 10);
      resolve(myNumber);
    }, 1000);
  });
}

const callIntervalFunc = () => { // need to wrap it with a function because setInterval is not a function and cannot call it from outside otherwise.
  const callInverval = setInterval(async()=>{
    console.log('checking the api...');

    var responseNumber = await callApi();
    console.log('Got response! ',responseNumber);
    if (responseNumber === 5) {
      clearInterval(callInverval);
      console.log('responseNumber is 5!! ends.');
    }
  }, 2000);
}

callIntervalFunc();

答案 4 :(得分:0)

在someFunction()中,它返回一个新的Promise,该Promise发出了resolve()并将经过处理的结果作为参数传递。在getpolledresult()中,捕获处理后的结果以确定是否进行轮询。

function getSearchResults(term) {
  return new Promise(resolve => {
    let timeout = 100 + Math.floor(Math.random() * 1900);
    console.log("is number < 500?", timeout);
    let result = {
      status: "",
      term_lower: term.toLowerCase(),
      term_upper: term.toUpperCase()
    };
    if (timeout < 500) {
      result.status = "DONE"
    }
    setTimeout(() => resolve(result), timeout);
  });
}

let cancelCallback = () => {};
let cancelflag = 0;

var sleep = (period) => {
  return new Promise((resolve) => {
    cancelCallback = () => {
      console.log("timeout...");
      // send cancel message...
      cancelflag = 1;
      return resolve('Canceled');
    }
    setTimeout(() => {
      resolve("tick");
    }, period)
  })
}

let asleep = async (period) => {
  let respond = await sleep(period);
  // if you need to do something as soon as sleep finished
  /* console.log("sleep just finished, do something...") */
  return respond;
}


function someFunction() {
  return new Promise((resolve) => {
    console.log("polling...");
    /* let a = client.get('/status'); */
    let a = getSearchResults('a');
    let processedResult = {
      status: ""
    };
    a.then(dataResult => {
      if (dataResult.status == "DONE") {
        //** want to get other values in dataResult here
        // and store it somewhere else for use later
        processedResult.status = "OK";
      };
      resolve(processedResult);
    });
  });
}


var getpolledresult = (promiseFn, period, timeout) => promiseFn().then((result) => {
  // ***want to work with results here.
  // need some way to get the status of what happened inside the .then(..) part
  //  eventually have to return success or failure and results to the frontend
  // (this part is already done)
  console.log(result);

  if (result.status !== "OK") {
    asleep(period).then((respond) => {
      // check if sleep canceled, if not, continue to poll
      if (cancelflag !== 1) {
        poll(promiseFn, period, timeout);
      }
    })
  }

});


var poll = (promiseFn, period, timeout) => {
  // just check if cancelCallback is empty function, 
  // if yes, set a time out to run cancelCallback()
  if (cancelCallback.toString() === "() => {}") {
    console.log("set timout...")
    setTimeout(() => {
      cancelCallback()
    }, timeout);
  }

  getpolledresult(promiseFn, period, timeout);
}


poll(someFunction, 1000, 10000);

答案 5 :(得分:0)

遵循continuousPromise方法将完成此工作,它需要2个参数:

  1. 负责提取数据的承诺

  2. 后续通话中的延迟(以毫秒为单位)

    let exit = false;
    const continuousPromise = (promise, interval)  => {
        const execute = () => promise().finally(waitAndExecute);
        const waitAndExecute = () => {
            if (exit) {
                return;
            }
            setTimeout(execute, interval)
        };
        execute();
    }
    

使用方法

continuousPromise(() => {
    return axios.get("something.json")
        .then((res) => {
            if (res && res.status !== 'PENDING') { // Check here for expected result
                exit = true;
            }
        })
        .catch((error) => {
            exit = true;
            console.log(error);
        })
}, 1000);

答案 6 :(得分:0)

这是基于帖子polling with async/await

的更可扩展的解决方案

只需添加以下实用程序方法:

const poll = async function (fn, fnCondition, ms) {
  let result = await fn();
  while (fnCondition(result)) {
    await wait(ms);
    result = await fn();
  }
  return result;
};

const wait = function (ms = 1000) {
  return new Promise(resolve => {
    setTimeout(resolve, ms);
  });
};

然后您可以这样称呼它:

let fetchReport = () => axios.get(reportUrl);
let validate = result => !result.data.summary;
let response = await poll(fetchReport, validate, 3000);