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

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

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

一些背景:

要按顺序复制文件,我的第一个想法是执行如下操作:

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 提供的答案是利用一个 确实 事实上 return 的函数一个承诺,而我的 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