简体   繁体   English

Q.js:如何在Q.js中重写异步系列流?

[英]Q.js: How can I rewrite an async series flow in Q.js?

In an attempt to grasp Q.js , I'd like to convert the following code using async.series in Q.js . 为了掌握Q.js ,我想在Q.js使用async.series转换以下代码。 Basically I create a folder if it doesn't exist (using mkdirp), move a file into a backup folder and save a file into a main folder. 基本上我创建一个文件夹,如果它不存在(使用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);
});

Actually I don't know where to start. 其实我不知道从哪里开始。 Thanks for your help. 谢谢你的帮助。

R. R.

The key is to convert the node.js functions to return promises using Q.denodeify before you start, this means the header of your file should look like: 关键是在开始之前将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);

That change wouldn't be needed if node.js natively supported promises. 如果node.js本身支持promise,则不需要进行此更改。

Option 1 选项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')
    });

I don't think it gets much simpler than that. 我认为它不比那简单得多。 Like @Bergi said though, it's more similar to "waterfall". 就像@Bergi所说,它更类似于“瀑布”。 If you want the exact behavior of series (but with promises) you'll have to use something like Option 2 or Option 3. 如果你想要系列的确切行为(但有承诺),你将不得不使用类似于选项2或选项3的东西。

Option 2 选项2

You could write out the code manually to save the results. 您可以手动编写代码以保存结果。 I usually find that, although this requires a little extra writing, it's by far the easiest to read: 我经常发现,虽然这需要一点额外的写作,但它是迄今为止最容易阅读的:

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);
    });

Option 3 选项3

If you find yourself using this sort of code all the time, you could write a very simple series helper (I've never found the need to do this personally): 如果你发现自己一直在使用这种代码,你可以编写一个非常简单的系列帮助器(我从未发现需要亲自执行此操作):

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);
});

I'd say once you've written the helper, the code is a lot clearer for promises than with all the error handling cruft required to work with callbacks. 我会说,一旦你编写了帮助程序,代码对于promises而言要比使用回调所需的所有错误处理要好得多。 I'd say it's clearer still when you either write it by hand or don't keep track of all those intermediate results. 当你手写或者不跟踪所有那些中间结果时,我会说它仍然更清晰。

Summing Up 加起来

You may or may not think these examples are clearer than the async.series version. 您可能会或可能不会认为这些示例比async.series版本更清晰。 Consider how well you might know that function though. 考虑一下你对这个功能的了解程度。 It's actually doing something pretty complex in a very opaque manner. 它实际上是以非常不透明的方式做一些非常复杂的事情。 I initially assumed that only the last result would be returned (ala waterfall) and had to look it up in the documentation of Async. 我最初假设只返回最后一个结果(ala waterfall),并且必须在Async的文档中查找它。 I almost never have to look something up int the documentation of a Promise library. 我几乎从来不需要在Promise库的文档中查找内容。

Make each of your functions return a promise. 让每个函数都返回一个承诺。 Construct them with a 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;
}

However, there are helper functions for node-style callbacks so that you don't need to check for the err yourself everytime. 但是, 节点式回调辅助函数,因此您不需要每次都自己检查err With Q.nfcall it becomes 随着Q.nfcall它变成了

function createOrSkip(folder) {
    return Q.nfcall(mkdirp, folder).then(function transform(dir) {
        return {created: !!dir, folderAt: backupFolder};
    });
}

The transform function will map the result ( dir ) to the object you expect. transform函数会将结果( dir )映射到您期望的对象。

If you have done this for all your functions, you can chain them with then : 如果你这样做了所有的功能,你可以把它们连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);
});

Notice that this works like async's waterfall , not series , ie the intermediate results will be lost. 请注意,这就像async的waterfall ,而不是series ,即中间结果将丢失。 To achieve that, you would need to nest them: 要实现这一点,您需要嵌套它们:

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);
});

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM