JavaScript:while循环中的异步方法
Posted
技术标签:
【中文标题】JavaScript:while循环中的异步方法【英文标题】:JavaScript: Asynchronous method in while loop 【发布时间】:2017-08-21 05:47:55 【问题描述】:我正在处理一个需要我将 javascript 与 API 方法调用一起使用的项目。我是一名 Java 程序员,之前从未做过 Web 开发,所以我遇到了一些麻烦。
此 API 方法是异步的,它处于一个 while 循环中。如果它返回一个空数组,则 while 循环结束。否则,它会循环。代码:
var done = true;
do
async_api_call(
"method.name",
// Do stuff.
,
function(result)
if(result.error())
console.error(result.error());
else
// Sets the boolean to true if the returned array is empty, or false otherwise.
done = (result.data().length === 0) ? true : false;
);
while (!done);
这不起作用。循环在“完成”的值更新之前结束。我已经对该主题进行了一些阅读,似乎我需要使用承诺或回调,因为 API 调用是异步的,但我不明白如何将它们应用于我上面的代码。
我们将不胜感激!
【问题讨论】:
你真的需要使用while循环吗?异步的想法。编程是为了避免循环直到完成某些事情,但使用回调(在您的情况下为function(result)
)来更新 UI。
您的async_api_call
我已经开除,所以它会通过,它不会;不等待回调。这不是异步调用的目的吗?先阅读这篇***.com/questions/748175/…,也许它会阐明异步函数的含义。而且因为您还不能更新 done 变量,所以 !done 是 false 并且会中断 do while 循环。
不幸的是,我需要循环 async_api_call
多次。这是因为有问题的方法只处理50个批次的数据,而我需要处理的项目有数千个,所以我需要继续循环它,直到处理完所有项目。
要理解的重要一点是,内部函数(function(result))将在稍后的某个时间点执行,可能在父函数完成执行很久之后; async_api_call 在异步操作完成并且 then 执行回调之前不会阻塞,它会更早返回并在工作完成时执行回调。已经有一些有帮助的答案,只是想说明一下。
这里可以用es6吗?
【参考方案1】:
编辑:看底部,有真正的答案。
我鼓励你使用Promise API。您的问题可以通过Promise.all
电话解决:
let promises = [];
while(something)
promises.push(new Promise((r, j) =>
YourAsyncCall(() => r());
);
//Then this returns a promise that will resolve when ALL are so.
Promise.all(promises).then(() =>
//All operations done
);
语法在 es6 中,这里是 es5 等价物(Promise API 可能包含在外部):
var promises = [];
while(something)
promises.push(new Promise(function(r, j)
YourAsyncCall(function() r(); );
);
//Then this returns a promise that will resolve when ALL are so.
Promise.all(promises).then(function()
//All operations done
);
您也可以让您的 api 调用返回 promise 并将其直接推送到 promise 数组。
如果您不想编辑 api_call_method,您可以随时将代码包装在新的 Promise 中,并在完成时调用方法 resolve。
编辑:我现在看到了你代码的重点,抱歉。我刚刚意识到 Promise.all 不能解决问题。
您应该将您发布的内容(不包括while循环和控制值)放入一个函数中,并根据条件再次调用它。
然后,所有内容都可以包装在一个 Promise 中,以便让外部代码知道这种异步执行。稍后我会在我的电脑上发布一些示例代码。
所以很好的答案
您可以使用 Promise 来控制应用程序的流程并使用递归而不是 while 循环:
function asyncOp(resolve, reject)
//If you're using NodeJS you can use Es6 syntax:
async_api_call("method.name", , (result) =>
if(result.error())
console.error(result.error());
reject(result.error()); //You can reject the promise, this is optional.
else
//If your operation succeeds, resolve the promise and don't call again.
if (result.data().length === 0)
asyncOp(resolve); //Try again
else
resolve(result); //Resolve the promise, pass the result.
);
new Promise((r, j) =>
asyncOp(r, j);
).then((result) =>
//This will call if your algorithm succeeds!
);
/*
* Please note that "(...) => " equivals to "function(...)"
*/
【讨论】:
如果你的目标是 es6,那么使用异步等待。这里的关卡太多 @EduardJacko 是的 async/await 非常适合这些东西,但是当创建答案时 async/await 我不知道它 实际上是他的 async_api_call 接受回调。无论如何,您都无法使用异步等待。对不起。【参考方案2】:如果您不想使用递归,您可以将 while
循环更改为 for of
循环并使用生成器函数来维护完成状态。这是一个简单的示例,其中for of
循环将等待异步函数,直到我们进行了 5 次迭代,然后将 done 翻转为 true。当您的 Web 服务调用缓冲了所有数据行时,您应该能够更新此概念以将您的 done 变量设置为 true。
let done = false;
let count = 0;
const whileGenerator = function* ()
while (!done)
yield count;
;
const asyncFunction = async function()
await new Promise(resolve => setTimeout(resolve); );
;
const main = new Promise(async (resolve)=>
for (let i of whileGenerator())
console.log(i);
await asyncFunction();
count++;
if (count === 5)
done = true;
resolve();
);
main.then(()=>
console.log('all done!');
);
【讨论】:
【参考方案3】:您也可以尝试递归解决方案。
function asyncCall(cb)
// Some async operation
function responseHandler(result)
if (result.error())
console.error(result.error());
else if(result.data() && result.data().length)
asyncCall(responseHandler);
asyncCall(responseHandler);
【讨论】:
【参考方案4】:sigmasoldier的solution是对的,只是想用async/await分享ES6版本:
const asyncFunction = (t) => new Promise(resolve => setTimeout(resolve, t));
const getData = async (resolve, reject, count) =>
console.log('waiting');
await asyncFunction(3000);
console.log('finshed waiting');
count++;
if (count < 2)
getData(resolve, reject, count);
else
return resolve();
const runScript = async () =>
await new Promise((r, j) => getData(r, j, 0));
console.log('finished');
;
runScript();
【讨论】:
漂亮,这引导我完成了我需要做的事情。我的特殊情况是我需要通过异步调用“循环”,但每个连续调用都取决于前一个调用返回的一些数据。添加更多变量并根据我的需要修改计数我让它工作。我唯一不完全理解的是有必要将 getData() 本身包装在一个 promise 中,如果 getData 本身返回一个 promise(因为 async func),为什么我们不能直接调用它并期望相同的结果? 我认为你是对的@xunux。不需要 promise,因为 async 函数应该原生返回一个 promise。 这对我来说是有意义的,但我需要这样做才能让它工作,我认为它是从所需的原始包装承诺中传递解析和拒绝功能,因为如果我刚刚做了await getData()
,即使它返回了一个承诺,我也无法通过它的解决和拒绝来结束每个循环【参考方案5】:
如果你不想使用Promises
,你可以像这样重构你的代码:
var tasks = [];
var index = 0;
function processNextTask()
if(++index == tasks.length)
// no more tasks
return;
async_api_call(
"method.name",
// Do stuff.
,
function(result)
if(result.error())
console.error(result.error());
else
// process data
setTimeout(processNextTask);
);
【讨论】:
【参考方案6】:你的循环不会工作,因为它是同步的,你的异步任务是异步的,所以循环将在异步任务响应之前完成。我建议你使用 Promises 来管理异步任务:
//first wrapping your API into a promise
var async_api_call_promise = function(methodName, someObject)
return new Promise((resolve, reject) =>
async_api_call(methodName, someObject, function(result)
if(result.error())
reject( result.error() )
else
resolve( result.data() )
);
)
现在到您的投票代码:
//a local utility because I don't want to repeat myself
var poll = () => async_api_call_promise("method.name", /*Do stuff.*/);
//your pulling operation
poll().then(
data => data.length === 0 || poll(), //true || tryAgain
err =>
console.error(err);
return poll();
).then((done) =>
//done === true
//here you put the code that has to wait for your "loop" to finish
);
为什么要承诺?因为他们对异步操作进行状态管理。为什么要自己实现?
【讨论】:
【参考方案7】: let taskPool = new Promise(function(resolve, reject)
resolve("Success!");
);
let that = this;
while (index < this.totalPieces)
end = start + thisPartSize;
if (end > filesize)
end = filesize;
thisPartSize = filesize - start;
taskPool.then(() =>
that.worker(start, end, index, thisPartSize);
);
index++;
start = end;
【讨论】:
【参考方案8】:这是我想出的解决方案。将其放在异步函数中。
let finished = false;
const loop = async () =>
return new Promise(async (resolve, reject) =>
const inner = async () =>
if (!finished)
//insert loop code here
if (xxx is done) //insert this in your loop code after task is complete
finshed = true;
resolve();
else
return inner();
await inner();
)
await loop();
【讨论】:
以上是关于JavaScript:while循环中的异步方法的主要内容,如果未能解决你的问题,请参考以下文章