Q.js:如何在 Q.js 中重写异步系列流程?
Posted
技术标签:
【中文标题】Q.js:如何在 Q.js 中重写异步系列流程?【英文标题】:Q.js: How can I rewrite an async series flow in Q.js? 【发布时间】:2013-09-01 19:59:51 【问题描述】:为了掌握Q.js
,我想在Q.js
中使用async.series
转换以下代码。基本上我会创建一个文件夹,如果它不存在(使用 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.
【问题讨论】:
【参考方案1】:让你的每个函数都返回一个承诺。使用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);
);
请注意,这类似于异步的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);
);
【讨论】:
谢谢,这很有趣。对于瀑布式操作,我发现 async 更具可读性(我猜这是个人喜好问题)。【参考方案2】:关键是在开始之前使用Q.denodeify
将node.js 函数转换为返回promise,这意味着您的文件头应如下所示:
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 原生支持 Promise,则不需要该更改。
选项 1
// 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 的东西。
选项 2
您可以手动编写代码以保存结果。我通常会发现,虽然这需要一些额外的写作,但它是迄今为止最容易阅读的:
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);
);
选项 3
如果你发现自己一直在使用这种代码,你可以编写一个非常简单的系列助手(我从来没有发现需要亲自这样做):
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);
);
我会说,一旦您编写了帮助程序,代码对于 Promise 来说要比处理回调所需的所有错误处理更加清晰。我想说的是,当您手动编写或不跟踪所有这些中间结果时,它会更清楚。
总结
您可能会也可能不会认为这些示例比async.series
版本更清晰。考虑一下您对该功能的了解程度。它实际上是以一种非常不透明的方式做一些非常复杂的事情。我最初假设只会返回最后一个结果(ala 瀑布),并且必须在 Async 的文档中查找它。我几乎不需要在 Promise 库的文档中查找内容。
【讨论】:
我同意你的观点,最新的选项更清晰。如果没有您的 promiseSeries 助手,我看不到以控制流方式使用 promise 的兴趣。 Promise 背后的模型更难掌握,我需要时间来适应。在我看来,使用异步的流控制更容易操作。不过我可能需要时间:) 问题是,async 提供的所有帮助器对于 promise 来说都是微不足道的,而且很少需要,以至于它们没有被放入库中。以上是关于Q.js:如何在 Q.js 中重写异步系列流程?的主要内容,如果未能解决你的问题,请参考以下文章
html 使用requirejs承诺基本示例(q.js和require.js应该在scripts文件夹中)