9

这是我的代码片段:

var processListing = function (directoryItems) {
    console.log('foreach');
    var itemsToDownload = [];
    directoryItems.forEach(function (element, index, array) {
        //Ignore directories
        if (element.type === 'd') {
            console.log('directory ' + element.name);
            return;
        }
        //Ignore non zips
        if (path.extname(element.name) !== '.zip') {
            console.log('ignoring ' + element.name);
            return;
        }
        //Download zip
        itemsToDownload.push({
            source: element.name,
            destination: element.name
        });
        //aftpSystem.downloadFile(element.name, element.name);
    });
    console.log('after foreach');
    return itemsToDownload;
};

var getFiles = function () {
    console.log('got files');
    return fs.readdirAsync(process.cwd() + "/zips/").then(function (files) {
        return files.filter(function (filename) {
            return path.extname(filename) === '.zip';
        });
    });
};

    aFtpClient. //this has been promisified
    listAsync(). //so calls methodAsync
    then(processListing).
    map(function (object) {
        return processItem(object).then(function (processResult) {
            return {
                input: object,
                result: processResult
            };
        });
    }).
    map(function (downloadItem) {
        console.log('downloading files');

        downloadItem.result.once('close', function () {
            console.log('closed');
        });

        return downloadItem.result.pipe(fs.createWriteStream(process.cwd() + "/zips/" + downloadItem.input.destination));
    }).
    then(getFiles).

我正在尝试使用承诺通过 FTP 下载项目。目前它下载第一个文件,但随后的文件失败。我是 node 新手,但相当有信心我的第二个 map 函数需要返回一个承诺,但是经过多次尝试后我无法弄清楚如何。我正在使用bluebird承诺,但看不到如何使用它和流。

你能指出我正确的方向吗?

谢谢

4

2 回答 2

15

我不确定您到底卡在哪里,但将您指向大方向就足够了:

  • 您有一个与管道和事件一起使用的接口
  • 您需要承诺该接口。

所以你需要做的是:

  1. 找出下载的“完成”事件是什么。
  2. 创建一个承诺并在该事件上解决它,在失败的事件上拒绝它。
  3. 回报那个承诺。

Promisifying 可以通过以下几种方式完成:

  • 由承诺库。Bluebird 包含一个非常聪明的 promisifier,它使用依赖于 JIT 的动态代码生成——它非常快——但它是为 NodeJS 的“nodeback”案例而构建的。(即错误作为回调的第一个参数传递。)

  • 使用Deferred对象。通常这种方式更容易出错。

  • 在 Bluebird 中使用Promise.method,这对于轻松地实现 API 非常有用,但在我们这里并不是这样。

  • 使用Promise构造函数。这就是我们将在这里做的。这也是标准投诉。

一般promise构造函数的接口是:

new Promise(function(resolve,reject){

    resolve(); // this resolves the promise
    reject(); // this rejets the promise
});

请注意,promisifying 事件发射器仅在它们触发完成事件并执行一次时才能正常工作。Promise 是一次性的,一旦完成,就不能改变状态。事件可以触发多次。"load"承诺诸如事件或事件之类的事情是完全可以的"finished"——但不要承诺重复多次的事情。

你的第二张地图应该是这样的:

map(function (downloadItem) {
    console.log('downloading files');

    downloadItem.result.once('close', function () {
        console.log('closed');
    });
    var pipeAction = downloadItem.result.pipe(fs.createWriteStream(process.cwd() + "/zips/" + downloadItem.input.destination));
    return new Promise(function(resolve,reject){
        pipeAction.on("end",function(){ //waits for data to be consumed
             // pipe has ended here, so we resolve the promise
             resolve();
        });
    });
}).

您通常应该将承诺提取到专用方法中。例如,上面可以是一个promisifyPipe或类似的。

于 2014-03-18T10:11:36.877 回答
2

这是一个承诺的管道:

//example: promisified_pipe(foo, fs.createWriteStream(somepath))enter code here
function promisified_pipe(response, file) {
let ended = false;

return new Promise(function(resolve, reject) {
    response.pipe(file);

    function nice_ending() {
      if (!ended) {
        ended = true;
        resolve();
      }
    }

    function error_ending() {
      if (!ended) {
        ended = true;
        reject("file error");
      }
    }

    file.on('finish', nice_ending);
    file.on('end', nice_ending);
    file.on('error', error_ending);
    file.on('close', error_ending);
  }).finally(() => file.close())
}
于 2016-06-14T03:27:12.680 回答