JavaScript:while循环中的异步方法

时间:2017-03-28 08:43:10

标签: javascript asynchronous while-loop

我正在处理一个项目,该项目要求我在API方法调用中使用JavaScript。我是一名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);

这不起作用。循环在“完成”的值更新之前结束。我已经完成了一些关于这个主题的阅读,看来我需要使用promises或回调,因为API调用是异步的,但是我无法理解如何将它们应用到我上面的代码中。

帮助将不胜感激!

8 个答案:

答案 0 :(得分:4)

编辑:看到底部,有真正的答案。

我鼓励你使用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.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(...){}"
 */

答案 1 :(得分:2)

如果您不想使用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);
            }
        }
    );
}

答案 2 :(得分:1)

您也可以尝试递归解决方案。

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);

答案 3 :(得分:1)

sigmasoldiersolution是正确的,只是想与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();

答案 4 :(得分:0)

您的循环无法正常工作,因为它是同步的,您的异步任务是异步的,因此循环将在异步任务甚至可以响应之前完成。我建议您使用Promises来管理异步任务:

if rank == 0:
    df = pickle.load(open('lipad0406.p', 'rb'))
    d = np.array(df, dtype=float)
    m = d.size / size
    comm.bcast(m)
else:
    d = None
    m = comm.bcast(None)

d_loc = np.zeros(m, dtype=float)

comm.Scatter(d,d_loc)

现在查看您的投票代码:

//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() )
            }
        });
    })
}

为什么要承诺?因为他们对异步操作进行状态管理。为什么要自己实现呢?

答案 5 :(得分:0)

如果不想使用递归,则可以将while循环更改为for of循环,并使用生成器函数来维护完成状态。这是一个简单的示例,其中for of循环将等待异步函数,直到我们进行了5次迭代,然后将done翻转为true。当您的网络服务调用已缓冲所有数据行时,您应该能够更新此概念以将您的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!');
});

答案 6 :(得分:0)

  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;
  }

答案 7 :(得分:0)

这是我想出的解决方案。将其放在异步函数中。


        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();