コードサンプルの約束は、リゾルバまたはリジェクトが常に呼び出されるわけではないため、返されません。実際には、resolve
は、i === 0
の場合にのみ呼び出されます。 Promises/A+ specificationによれば、約束は、resolve
を呼び出すことによってfulfilled
状態にのみ遷移することができる。 reject
を呼び出すか、エグゼキュータ内から例外をスローすることによってのみ、rejected
状態に遷移することもできます。したがって、呼び出すことなくエグゼキュータの最後に到達するか、コールバックとして渡すことによって、約束は確実にpending
状態にとどまります。
少しのリファクタリングで達成することができます。
...各FTPサーバを介して
- 順次新しいもの
を決定するためにローカルに格納されたファイルのリストを比較ファイル
- のリストについては、与えられたディレクトリを読む:あなたの目標として、以下の考慮
- 新しいものがある場合は、順次
- リターンにすべて新しくダウンロードしたファイルのリストを、それらをダウンロードし
データ
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);
});
それはいくつかの場所で少し複雑に見えるかもしれませんが、各機能のアクションはよりモジュールです。多少直感的ではないアイデアは、Array.prototype.reduce
を使用して、データの配列を順次実行される約束事の配列に変えることです。
ファイルのダウンロードを約束すると、すぐにファイルをダウンロードしようとするため、一度に1つずつダウンロードする予定がある場合、すぐにすべての約束を作成することはできません。それ以外の場合は、シーケンスがややシンプルに見えます。
'()'の内部呼び出しを見てください。 Operateは約束を返しますが、何もしていません。 –
これはまさに私が何を言っているのか理解していれば、私は再びサーバーを繰り返し処理できるからです。それが終わったら(最初にテストされているように)、それが解決されます。私はそれで何ができますか? note:https://github.com/dkran/FloorCoveringEDI/blob/combinator/app.js – dkran
'if(servers [i])'が 'true'なら' resolve'を呼び出すことは決してありません。おそらく、内部の '操作()'呼び出しが返す約束が解決した後で解決したいと思うかもしれませんか? –