我有以下功能。它反复调用自身并通过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)
})
答案 0 :(得分:1)
代码示例中的promise不会返回,因为并不总是调用它们的解析器或拒绝器。实际上,resolve
仅在i === 0
时调用。根据{{3}},承诺只能通过调用fulfilled
转换为resolve
状态。它也只能通过调用rejected
或从执行程序中抛出异常来转换到reject
状态。因此,在不调用或传递一个作为回调的情况下到达执行程序的末尾可确保承诺无限期地保持在pending
状态。
您寻求的目标可以通过一点点重构来实现。考虑以下因素:
数据
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数组。
由于创建下载文件的承诺会立即尝试下载文件,如果有人打算一次下载文件,则无法立即创建所有承诺。否则,序列可能看起来有点简单。