JavaScript循环遍历数组异步化

4
我正在编写一个针对运行在NodeJS上的游戏的机器人代码,这个函数的作用是循环遍历一个向量数组,并使机器人去到每个向量。
然而,实际上它告诉机器人同时奔向所有向量,因此机器人会发疯并最终只跑到数组中的最后一个向量。
function digSchedule() {
    var arrayLength = blocksToMine.length;
    for (var i = 0; i < blocksToMine.length; i++) {
        console.log(i);
        scaffoldTo(blocksToMine[i]);
    }
    ...
}

需要运行函数scaffoldTo(),然后等待机器人完成该功能,然后再为数组中的下一个元素运行它,但我不知道如何做到。


嗨D,欢迎来到SO!格式良好的问题和示例代码 - 真棒 :) - Ben
1
阅读有关异步Promise的内容。 - Scott Stensland
忘了提一下,我是在NodeJS上运行这个程序的,它不让我编辑原始帖子,我也不知道为什么。 - D. Johnson
我假设scaffoldTo()是异步的?它是如何实现的?循环如何确定函数何时“完成”? - Thomas
@ScottStensland - 与同步 Promise 相反? :p - Jaromanda X
5个回答

1
有几种方法可以实现这个。第一种可能是通过传递一个回调函数来实现“下一个要调用的函数”(可能是)。您可以使用.bind()创建一个具有迭代器索引的引用。

或者,您可以设置一组Promise循环,根据定义,它们具有在解决承诺后执行的.then()方法。

最后,async/await模式类似于Promises,但有些人发现它更清晰,并且似乎正在赢得炒作战争: https://hackernoon.com/6-reasons-why-javascripts-async-await-blows-promises-away-tutorial-c7ec10518dd9.

回调函数(解决方案1)将在任何版本的JS中可用。 Promise通常可用于库,并且在ES6中具有本机支持。 Async/await是ES2017中的一个提议(?),并且得到了广泛的支持。


你能否编辑这篇文章,加上我正在使用NodeJS吗?在你编辑之后,我就不能再编辑了。 - D. Johnson

0

这是一个很好的使用ES2017 async functions的案例。

请尝试以下操作:

async function digSchedule() {
    var arrayLength = blocksToMine.length;
    for (var i = 0; i < blocksToMine.length; i++) {
        console.log(i);
        await scaffoldTo(blocksToMine[i]);
    }
    ...
}

如果ES2017不可行,你最好的选择是创建一个递归函数,只有在来自的Promise被解决时才会再次调用自身。

我正在使用NodeJS,出现了这个错误:SyntaxError: Unexpected token function。 - D. Johnson
4
异步函数是 ES2017 标准,而不是 ES6。原文链接:https://github.com/tc39/proposals/blob/master/finished-proposals.md - Delapouite
你需要 Node 7.6.0 或更新版本。 - joews
1
而递归并不是必需的;async/await 只是 Promise 语法糖。你可以使用继续传递回调或者 Promise 数组作为结果。 - joews

0

这里有另一种你可以尝试的方法。这种方法更注重回调风格,但它假定你可以修改scaffoldTo函数以及digSchedule所需的参数。

function digSchedule(i, callback) {
   if(!i){
      i = 0;
   }
   if(i < blocksToMine.length){
      scaffoldTo(blocksToMine[i], digSchedule(i++, callback));
   }
   else{
     callback();
   }
}

然后在scaffoldTo内部,你需要像这样的东西

function scaffoldTo(block, callback){
    //do what ever you need to have the bot go to the vector
    //after bot goes to vector call callback
    callback();
}

为了开始所有这一切,您只需要像这样调用digSchedule:

digSchedule({null or 0}, function(){
   console.log("finished visiting vectors");
});

这确实改变了使用 for 循环的模式,但我认为这也是实现目标的有趣方式。


0
您可以使用async模块来实现此操作。或者,您也可以尝试类似以下的方法。
function forEachAsync(array, fun, cb) {
        var index = 0;
        if (index == array.length) {
                cb(null);
                return;
        }

        var next = function () {
                fun(array[index], function(err) {
                        if (err) {
                                cb(err);
                                return;
                        }
                        index++;
                        if (index < array.length) {
                                setImmediate(next);
                                return;
                        }

                        //We are done
                        cb(null);
                });
        };

        next();
}

forEachAsync([1,2,3,4,5], function(e, cb) {
        console.log(e);
        cb();
}, function(err) {
        console.log('done');
});

0

以下是我们如何借助 Promise 来实现。

let numbers = new Array(1,3,2,1);
function asyncFunction(number){
    return new Promise((resolve,reject)=>{
        setTimeout(()=>{
            console.log("Number : ",number);
            return resolve();
        },number*1000);
    })
  
}
let promise = Promise.resolve();
// Here we are using forEach to chain promise one after another.
numbers.forEach(number=>{
    promise = promise.then(()=>{
        return asyncFunction(number);
    });
})
promise.then(()=>{
    console.log("Every thing is done!");
})

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接