3

我想知道 node.js 是否对异步调用开始/完成的顺序做出任何保证。

我不认为是这样,但是我已经阅读了 Internet 上的一些代码示例,我认为这些代码示例有问题,因为异步调用可能不会按预期的顺序完成,但是这些示例通常在节点有多棒的上下文中说明因为它的单线程异步模型。但是,我找不到这个一般问题的直接答案。

是不是不同节点模块做出不同保证的情况?例如在https://stackoverflow.com/a/8018371/1072626,答案清楚地说明了涉及 Redis 的异步调用保留了顺序。

这个问题的症结可以归结为以下执行(或类似的)在节点中是严格安全的吗?

var fs = require("fs");
fs.unlink("/tmp/test.png");
fs.rename("/tmp/image1.png", "/tmp/test.png");

根据作者的说法,unlink需要调用,因为如果存在预先存在的文件,重命名将在 Windows 上失败。但是,这两个调用都是异步的,所以我最初的想法是调用rename应该在回调中,unlink以确保异步 I/O 在异步rename操作开始之前完成,否则rename可能会先执行,导致错误。

4

2 回答 2

4

异步操作没有任何确定的执行时间。

当您调用 时unlink,它会要求操作系统删除文件,但未定义操作系统何时实际删除文件;可能是一毫秒或一年后。

异步操作的全部意义在于它们不相互依赖,除非明确说明。

为了在rename之后发生unlink,您必须像这样修改您的代码:

fs.unlink("/tmp/test.png", function (err) {
    if (err) {
        console.log("An error occured");
    } else {
        fs.rename("/tmp/image1.png", "/tmp/test.png", function (err) {
            if (err) {
                console.log("An error occured");
            } else {
                console.log("Done renaming");
            }
        });
    }
});

或者,使用同步版本的 fs 函数(注意这些会阻塞执行线程):

fs.unlinkSync("/tmp/test.png");
fs.renameSync("/tmp/image1.png", "/tmp/test.png");

还有诸如async之类的库可以使异步代码看起来更好:

async.waterfall([
    fs.unlink.bind(null, "/tmp/test.png");
    fs.rename.bind(null, "/tmp/image1.png", "/tmp/test.png");
], function (err) {
    if (err) {
        console.log("An error occured");
    } else {
        console.log("done renaming");
    }
});

请注意,在所有示例中,错误处理都非常简化以表示该想法。

于 2012-06-28T04:33:51.280 回答
1

如果您查看 Node.js 的文档,您会发现函数 fs.unlink 将回调作为参数:

 fs.unlink(path, [callback]);

您打算在当前函数返回时执行的操作应作为回调参数传递给函数。因此,通常在您的情况下,代码将采用以下形式:

 var fs = require("fs");
 fs.unlink("/tmp/test.png", function(){
     fs.rename("/tmp/image1.png", "/tmp/test.png");
 });

在取消链接和重命名的特定情况下,Node.js 中也有同步功能,可以用作 fs.unlinkSync(path) 和 fs.renameSync(oldPath, newPath)。这将确保代码同步运行。

此外,如果您希望使用异步实现但保持更好的可读性,您可以考虑使用async 之类的库。它还具有不同实现模式的选项,例如并行、串行、瀑布等。

希望这可以帮助。

于 2012-06-28T04:33:17.723 回答