并行运行多个异步函数的安全方法?

时间:2017-07-12 17:09:41

标签: javascript async-await

我正在编写一些代码来扫描目录,而我发现这可能不是最好的主意:

files.forEach(async fileName => {
  stat = await lstat(fileName);
});

因为我要同时为目录中的每个文件触发一个lstat。有谁知道"清洁"这样做的方法?我正在考虑一个维护队列并将其排干的lib。

我知道一些" old"异步库执行此操作,但我不知道使用本机异步/等待调用执行此操作的任何内容

2 个答案:

答案 0 :(得分:3)

一般来说,没有代码可以并行运行,因此几百个公开承诺不应该成为问题。

如果你想一个接一个地运行,而不是简单的for循环:

async function iterate(){
 for(var i=0;i<files.length;i++){
  stat = await lstat(files[i]);
 }
}

要一次运行多个,但并非所有人都可以这样做:

async function iterate(){
 var atonce=10;
 for(var i=0;i<files.length;i+=atonce){
  stats = await Promise.all(files.slice(i,i+atonce).map(file=>lstat(file));
 }
}

另一种方式是一些Promise队列:

var current=0;
async function retrieve(){
 if(current>=files.length) return;
 current++;
 await lstat(files[current-1]);
 retrieve();
}
retrieve();//two in parallel
retrieve();

如果要并行运行,可以使用Promise.all来捕获结果(取决于用例):

Promise.all(files.map(async function(file){
 return await lstat(file);
}).then(results=>...);

答案 1 :(得分:0)

你在找这样的东西吗?

//sry, but I didn't get up with a beter name
function Todo(numParalell = 8){
	var todo = [], running = 0;
		
	function init(resolve){
		if(running < numParalell){
			++running;
			resolve();
		}else{
			todo.push(resolve);
		}
	}
	
	function next(){
		if(todo.length){
			todo.shift()();		//FIFO
			//todo.pop()();		//LIFO / FILO
		}else{
			--running;
		}
	}
	
	return {
		get numRunning(){ return running },
		get numWaiting(){ return todo.length },
		append(fn){
			if(typeof fn !== "function"){
				//fn ain't a function but a value
				return Promise.resolve(fn);
			}
				
			var promise = new Promise(init).then(fn);
			promise.then(next, next);
			return promise;
		}
	}
}

//a queue that runs 4 tasks in paralell
var todo = Todo(4);

//some task that just kills some time
var someAsyncTask = v => new Promise(resolve => setTimeout(resolve, Math.random() * 5000, v));

//add 25 items to the todo-list 
//and log the start and end time from now
var promises = Array(25).fill(Date.now())
  .map((t,i) => todo.append(() => {
    console.log('+starting', i, 'after', Date.now() - t, 'ms');

    return someAsyncTask()
      .then(() => console.log('-finished', i, 'after', Date.now() - t, 'ms'))
      .then(() => 'resolve to ' + i);
  }));

Promise.all(promises).then(arr => console.log("And the resolved promises:", arr));
.as-console-wrapper{top:0;max-height:100%!important}

Todo#append()接受一个函数来执行您想要管理的异步任务,并返回带有结果的Promise。虽然该功能不会立即执行,但只要有空槽即可。

请注意,此代码无法解析依赖性,例如

var todo = Todo(1);
todo.append(() => todo.append(() => ...)) 

因此,您可能会遇到外部任务没有完成,因为它取决于/解析内部任务。内部人可能无法启动,因为此Todo列表中没有空闲插槽。