承诺不能正常回归

时间:2016-03-25 18:02:06

标签: javascript node.js promise

我有以下功能。它反复调用自身并通过ftp服务器迭代检查新文件。我试图让它成为一个承诺,以便我可以operate().then(function(newFilesObject),但我无法让.then启动操作。它确实试图解决它但不发送。顺便说一下,newFiles是一个全局变量,它可以获取附加到每个服务器的文件。如果需要更多代码,我可以发布或github。

function operate(){
  return new Promise(function(resolve, reject){
    if(servers[i]){
      if(i!== 0) ftp = new JSFtp(servers[i].server)
      local = servers[i].local
      remote = servers[i].remote
      localFiles = fs.readdirSync(local)
    }else{
      console.log('trying to resolve')
      console.log(newFiles)
      resolve(newFiles)
    }
    gatherFiles(remote).then(function(files){
      if(files.length>0){
        downloadNew(files).then(function(){
          console.log('Done: ' + servers[i].server.host)
          i++
          operate()
        })
      }else{
        console.log('No updates: ' + servers[i].server.host)
        i++
        operate()
      }
    })
  })
}

operate().then(function(files){
console.log('files: ' + files)
})

1 个答案:

答案 0 :(得分:1)

代码示例中的promise不会返回,因为并不总是调用它们的解析器或拒绝器。实际上,resolve仅在i === 0时调用。根据{{​​3}},承诺只能通过调用fulfilled转换为resolve状态。它也只能通过调用rejected或从执行程序中抛出异常来转换到reject状态。因此,在不调用或传递一个作为回调的情况下到达执行程序的末尾可确保承诺无限期地保持在pending状态。

您寻求的目标可以通过一点点重构来实现。考虑以下因素:

  1. 按顺序通过每个FTP服务器......
  2. 读取给定目录以获取文件列表
  3. 将文件列表与本地存储的文件列表进行比较以确定新文件
  4. 如果有新的,请按顺序下载
  5. 返回所有新下载文件的列表
  6. 数据

    var knownFTPServers = [{
      'localDirectory': 'sub/',
      'localFilepaths': ['docA.json', 'docB.json'],
      'remoteDirectory': 'remsub/',
      'remoteFilepaths': [],
      'jsftpHandle': undefined,
      'host': 'example.com'
    },
    {
      'localDirectory': 'root/',
      'localFilepaths': ['file1.txt', 'file2.txt'],
      'remoteDirectory': 'remroot/',
      'remoteFilepaths': [],
      'jsftpHandle': undefined,
      'host': 'geocities.com'
    }];
    

    逻辑

    function pullNewFilesFromFTPServer(ftpServer) {
      return new Promise(function (resolve, reject) {
        var handle = new JSFtp(ftpServer);
        ftpServer.jsftpHandle = new JSFtp(ftpServer);
    
        // Returns a promise for reading a directory from JSFtp server
        // resolves with file list
        // rejects with FTP error
        function readdir(directory) {
          return new Promise(function (resolve, reject) {
            handle.ls(ftpServer.remoteDirectory, function (err, res) {
              if (err) return reject(err);
              resolve(res);
            });
          });
        }
    
        // Returns a promise for downloading a file from a remote JSFtp server
        // resolves with the filepath of the downloaded filepath
        // rejects with FTP error
        function downloadFile(path) {
          return new Promise(function (resolve, reject) {
            handle.get(path, path, function (err) {
              if (err) return reject(err);
              resolve(path);
            });
          });
        }
    
        // get all remote filepaths on server
        readdir(ftpServer.remoteDirectory)
    
        // filter out filepaths already present locally
        .then(function (remoteFilepaths) {
          return remoteFilepaths.filter(function (path) {
            return ftpServer.localFilepaths.indexOf(path) < 0;
          });
        })
    
        // download new filepaths sequentially
        // reduce turns the array of new filepaths into a promise chain
        // return new filepaths after completing the promise chain
        .then(function (newFilepaths) {
          return newFilepaths.reduce(function (previousDownloadPromise, newPath) {
            return previousDownloadPromise.then(function () {
              return downloadFile(newPath);
            });
          }, Promise.resolve())
          .then(function () { return newFilepaths; });
        })
    
        // resolve server promise with new filepaths or reject with errors
        .then(resolve, reject);
      });
    }
    
    var allFilesDownloaded = [];
    knownFTPServers.reduce(function (previousServerPromise, server) {
      return previousServerPromise.then(function (filesDownloaded) {
        allFilesDownloaded = allFilesDownloaded.concat(filesDownloaded);
        return pullNewFilesFromFTPServer(server);
      });
    }, Promise.resolve([]))
    
    .then(function () {
      console.log(allFilesDownloaded);
    }, function (err) {
      console.err(err);
    });
    

    虽然在某些地方看起来有点复杂,但每个功能的动作都更加模块化。有点不直观的想法是使用Promises/A+ specification将数据数组转换为顺序执行的promises数组。

    由于创建下载文件的承诺会立即尝试下载文件,如果有人打算一次下载文件,则无法立即创建所有承诺。否则,序列可能看起来有点简单。