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

时间:2015-11-05 16:09:00

标签: javascript node.js asynchronous q edge.js

我正在尝试在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提供的答案是利用的功能实际上返回了一个承诺,而我的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());

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

2 个答案:

答案 0 :(得分:3)

也许这样的事情可以解决问题:

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

答案 1 :(得分:0)

在数组上使用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