轮询直到得到具体结果?
Posted
技术标签:
【中文标题】轮询直到得到具体结果?【英文标题】:Polling until getting specific result? 【发布时间】:2017-09-13 22:56:41 【问题描述】:我目前正在尝试使用此链接 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/等)
感谢任何提示/建议/帮助。谢谢!
另外,我正在处理的应用程序不使用 JQuery。
【问题讨论】:
【参考方案1】:这是一个基于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);
【讨论】:
最干净的解决方案!谢谢 那么这是否意味着如果fn
需要很长时间才能完成,那么在fn
返回之前不会进行下一次投票,对吗?例如,假设fetchReport
需要 6 秒才能完成,ms
是 3000(或 3 秒)。这意味着 API 调用需要 6 秒 > 再等待 3 秒以进行下一次 API 调用 > 再需要 6 秒 > 再等待 3 秒,等等。所以这意味着每次轮询之间的间隔是 9 秒。我理解对了吗?
@7ball,是的,没错。如果您有一个运行时间很长的 API,那么无论它花费的时间都将包含在每个循环中。您可以每n
秒以 interval 触发一些东西,但最好避免重叠相同的调用。如果您的 API 调用需要 6 秒,并且您不想等待其他 3 秒,则可以减少额外的等待时间
不,这实际上正是我所需要的,非常感谢!就您的观点而言,如果前一个民意调查仍在进行,我不希望下一次民意调查发生。我只想确保至少每 3 秒调用一次 API。【参考方案2】:
Node.js 的最新版本,支持 async / await。
下面是一个使用它的例子..
async/await 的一大优势,就是很容易跟上代码,理解它的逻辑。例如,如果您想扩展此功能,以获得最大尝试的功能,那将是微不足道的。 (提示)这只是一个 for 循环 :)
let count = 0;
var client =
get: function ()
return new Promise(function (resolve, reject)
count ++;
setTimeout(function ()
if (count > 4) resolve(status:'DONE',otherStuff:'Other Stuff');
else resolve(status: `count: $count`);
, 1000);
);
async function someFunction()
while (true)
let dataResult = await client.get('/status');
console.log(dataResult.status);
if (dataResult.status == "DONE")
return dataResult;
(async () => let r = await someFunction(); console.log(r); )();
【讨论】:
【参考方案3】:这是一个函数示例,该函数使用 promises 和 polls 直到你得到想要的结果。我还对其进行了参数化,以便您可以传入轮询间隔和超时值:
// 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
);
这里的关键是链接你的 Promise,这样每次你再次调用 run()
时,你都会返回它的值,这样它就会被链接到之前的 Promise 上。然后,每当你最终返回一个值或抛出一个异常时,原始的 Promise 都会将该值或错误作为已解决的值或被拒绝的原因。
请注意,我添加了一个超时,因为你真的不想永远轮询,特别是在某些不可预见和重复出现的错误可能不会拒绝承诺,但不会得到你想要的结果的情况下。
【讨论】:
如何重构不嵌套承诺? @Jonathon - 你在说什么嵌套?这是伪递归的,不是嵌套的。如果您要在.then()
内进行条件分支,您几乎必须按照这里的方式进行。我不明白您要删除什么嵌套?
很抱歉 - 发布到错误的页面 - 一年前的帖子令人印象深刻的响应时间;)【参考方案4】:
我只需要解决一个类似的问题。 以下是我的解决方案的要点:
// 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();
【讨论】:
谢谢!这个解决方案对我有用。对其他人来说可能很明显,但如果您在callApi
的内部等待 API 响应,则需要在传递给 new Promise
的函数之前添加 async
。【参考方案5】:
以下continuousPromise
方法将完成这项工作,它需要2个参数:
Promise 负责获取数据
后续调用的延迟(以毫秒为单位)
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】:一种选择是将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)
)
【讨论】:
【参考方案7】:在 someFunction() 中,它返回一个新的 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);
【讨论】:
【参考方案8】:这里有一个简单的替代方法
(function poll()
//do the check - ajax, etc.
if (condition_that_means_were_done)
return
setTimeout(poll,timeout_ms)
)();
【讨论】:
以上是关于轮询直到得到具体结果?的主要内容,如果未能解决你的问题,请参考以下文章