如何正确地按顺序调用异步函数列表

How can I properly call a list of async functions in order?

本文关键字:异步 函数 列表 顺序调用 正确地      更新时间:2023-09-26

我正试图在Node.js中编写一个类似"robocopy/mir"的函数,但似乎无法理解如何按顺序正确执行几个异步函数。

一些背景:

  • 该脚本是在Windows上运行的,因此,我需要找到一些方法来复制文件,同时保留修改时间和接收进度通知
  • 为了解决这个问题,我继续&在.NET中编写了我的复制函数(用Edge.js调用它)——这个复制函数只是回调一个报告文件复制进度的Node函数。这件作品完美无瑕

为了按顺序复制文件,我的第一个想法是做以下事情:

Object.keys(filesToCopy).forEach(function(key) {
    var deferred = q.defer();
    var payload = {
        sourcePath: key,
        destPath: filesToCopy[key],
        progressCallback: progressCallback
    };
    console.log('Copying %s...', sourcePath);
    // Edge.js called here
    copyFile(payload, deferred.makeNodeResolver());
    deferred.promise.then(function(result) {
        console.log('%s complete.', result);
    }, function(err) {
        console.error('Error: ', err.message);
    });
    promises.push(deferred.promise);
});

不幸的是,一旦调用.NET函数,它(正如预期的那样)就会开始复制每个文件,因此,我会同时收到所有文件的进度通知,并给出如下输出:

1%
2%
1%
2%
3%
3%

我似乎需要一种方法,在一次完成所有工作之前,将要完成的工作排队,每一项都在下一项工作进行之前完成。当所有项目都完成后,我需要得到通知。这个解决方案看起来很简单,但我仍然无法解决,因为我尝试的每一个角度都会带来另一个问题。如有任何帮助,我们将不胜感激,谢谢!

编辑:正如我在评论中所说,Bergi提供的答案是使用了一个函数,而确实返回了一个promise,而我的Edge.js函数没有。我能够首先通过使用filesToCopy的数组而不是对象来解决我的问题,然后做一些类似的事情:

  return filesToCopy.reduce(function(prev, curr) {
    return prev.then(function() {
      var deferred = q.defer();
      copyFile(curr, function(err, result) {
        deferred.resolve(result);
        console.log('Completed %s', result);
      });
      return deferred.promise;
    })
  }, q());

这可能不是最好的方法,但它适用于我的用途。

也许这样的东西会起作用:

var $j = function(val, space) {
  return JSON.stringify(val, null, space || '')
}
var log = function(val) {
  document.body.insertAdjacentHTML('beforeend', '<div><pre>' + val + '</div></pre>')
}
var files = '12345'.split('').map(function(v) {
  return {
    name: 'file_' + v + '.js',
    load: function() {
      var cur = this;
      var pro = new Promise(function(resolve, reject) {
        log('loading : ' + cur.name);
        
        // we simualate the loading stuff
        setTimeout(function() {
          resolve(cur.name);
        }, 1 * 1000);
      }).then( function( val ) {
        
        // once loaded 
        log('loaded : ' + val);
        return val;
      
      });
      return pro;
    }
  };
});
files.reduce(function(t, v) {
  t.promise = t.promise.then(function(){ 
      return v.load();
  });
  
  return t;
}, {
  promise: Promise.resolve(1)
});

对数组使用async.eachSeries,对对象使用async.forEachOfSeries

循环对象

var async = require('async');
var filesObject = {'file/path/1': {}, 'file/path/2': {}};
async.forEachOfSeries(filesObject, copyFileFromObj, allDone);
function copyFileFromObj(value, key, callback) {
  console.log('Copying file ' + key + '...');
  callback(); // when done
}
function allDone(err) {
  if (err) {
    console.error(err.message);
  }
  console.log('All done.');
}                

循环数组

var async = require('async');
var filesArray = ['file/path/1', 'file/path/2'];
async.eachSeries(filesArray, copyFile, allDone);
function copyFile(file, callback) {
  console.log('Copying file ' + file + '...');
  callback(); // when done
}
function allDone(err) {
  if (err) {
    console.error(err.message);
  }
    console.log('All done.');
}

此处的工作示例:https://tonicdev.com/edinella/sync-loop-of-async-operations