为了抓住Q.js
,我想使用async.series
中的Q.js
转换以下代码。基本上我创建一个文件夹,如果它不存在(使用mkdirp),将文件移动到备份文件夹并将文件保存到主文件夹。
var async = require('async');
var fs = require('fs');
var path = require('path');
var sessiondId = new Date().getTime() % 2 == 0 ? new Date().getTime().toString() : '_1234';
var backupFolder = path.join(__dirname,sessiondId);
var backupFullPath = path.join(backupFolder,'a.txt');
var fullPath = path.join(__dirname,'main','a.txt');
var mkdirp = require('mkdirp');
async.series({
createOrSkip: function(callback) {
mkdirp(backupFolder, function (err, dir) {
if(err) {
callback(err, null);
} else {
callback(null, {created: !!dir, folderAt: backupFolder});
}
});
},
move: function(callback) {
fs.rename(fullPath, backupFullPath, function(err) {
if(err) {
callback(err, null);
} else {
callback(null, {backupAt: backupFullPath});
}
});
},
write: function(callback) {
fs.writeFile(fullPath, 'abc', function(err) {
if (err) {
callback(err, null);
} else {
callback(null, {saveAt: fullPath});
}
});
}
}, function(err, result) {
console.log(result);
});
其实我不知道从哪里开始。谢谢你的帮助。
R上。
答案 0 :(得分:10)
关键是在开始之前将node.js函数转换为使用Q.denodeify
返回promises,这意味着文件的标题应如下所示:
var Q = require('q')
var fs = require('fs');
var path = require('path');
var sessiondId = new Date().getTime() % 2 == 0 ? new Date().getTime().toString() : '_1234';
var backupFolder = path.join(__dirname,sessiondId);
var backupFullPath = path.join(backupFolder,'a.txt');
var fullPath = path.join(__dirname,'main','a.txt');
var mkdirp = Q.denodeify(require('mkdirp'));
var rename = Q.denodeify(fs.rename);
var writeFile = Q.denodeify(fs.writeFile);
如果node.js本身支持promises,则不需要进行更改。
// createOrSkip
mkdirp(backupFolder)
.then(function (dir) {
// move
return rename(fullPath, backupFullPath);
})
.then(function () {
// write
return writeFile(fullPath, 'abc');
})
.done(function () {
console.log('operation complete')
});
我认为这不比那简单得多。就像@Bergi所说,它更类似于“瀑布”。如果你想要系列的确切行为(但有承诺),你将不得不使用类似于选项2或选项3的东西。
您可以手动编写代码以保存结果。我经常发现,虽然这需要一点额外的写作,但它是迄今为止最容易阅读的:
var result = {}
mkdirp(backupFolder)
.then(function (dir) {
result.createOrSkip = {created: !!dir, folderAt: backupFolder};
return rename(fullPath, backupFullPath);
})
.then(function () {
result.move = {backupAt: backupFullPath};
return writeFile(fullPath, 'abc');
})
.then(function () {
result.write = {saveAt: fullPath};
return result;
})
.done(function (result) {
console.log(result);
});
如果你发现自己一直在使用这种代码,你可以编写一个非常简单的系列助手(我从未发现需要亲自执行此操作):
function promiseSeries(series) {
var ready = Q(null);
var result = {};
Object.keys(series)
.forEach(function (key) {
ready = ready.then(function () {
return series[key]();
}).then(function (res) {
result[key] = res;
});
});
return ready.then(function () {
return result;
});
}
promiseSeries({
createOrSkip: function () {
return mkdirp(backupFolder).then(function (dir) {
return {created: !!dir, folderAt: backupFolder};
});
},
move: function () {
return rename(fullPath, backupFullPath)
.thenResolve({backupAt: backupFullPath});
},
write: function () {
return writeFile(fullPath, 'abc')
.thenResolve({saveAt: fullPath});
}
}).done(function (result) {
console.log(result);
});
我会说,一旦你编写了帮助程序,代码对于承诺来说比使用回调所需的所有错误处理要好得多。当你手写或者不跟踪所有这些中间结果时,我会说它更清晰。
您可能会或可能不会认为这些示例比async.series
版本更清晰。考虑一下你对这个功能的了解程度。它实际上是以非常不透明的方式做一些非常复杂的事情。我最初假设只返回最后一个结果(ala waterfall),并且必须在Async的文档中查找它。我几乎不必在Promise库的文档中查找内容。
答案 1 :(得分:2)
让每个函数都返回一个promise。使用Deferred:
构建它们function createOrSkip(folder) {
var deferred = Q.defer();
mkdirp(folder, function (err, dir) {
if(err) {
deferred.reject(err);
} else {
deferred.resolve({created: !!dir, folderAt: backupFolder});
}
});
return deferred.promise;
}
但是,有helper functions for node-style callbacks因此您无需每次都自己检查err
。 Q.nfcall
成为
function createOrSkip(folder) {
return Q.nfcall(mkdirp, folder).then(function transform(dir) {
return {created: !!dir, folderAt: backupFolder};
});
}
transform
函数会将结果(dir
)映射到您期望的对象。
如果您已针对所有功能执行此操作,则chain them可以使用then
:
createOrSkip(backupfolder).then(function(createResult) {
return move(fullPath, backupFullPath);
}).then(function(moveResult) {
return write(fullPath, 'abc');
}).then(function(writeResult) {
console.log("I'm done");
}, function(err) {
console.error("Something has failed:", err);
});
请注意,这就像async的waterfall
,而不是series
,即中间结果将会丢失。要实现这一点,您需要嵌套它们:
createOrSkip(backupfolder).then(function(createResult) {
return move(fullPath, backupFullPath).then(function(moveResult) {
return write(fullPath, 'abc');.then(function(writeResult) {
return {
createOrSkip: createResult,
move: moveResult,
write: writeResult
};
});
});
}).then(function(res){
console.log(res);
}, function(err) {
console.error("Something has failed:", err);
});