【问题标题】:Could Node feasibly return a value from a function call before completing all operations within the function itself?在完成函数本身的所有操作之前,Node 是否可以从函数调用中返回一个值?
【发布时间】:2023-03-13 04:57:01
【问题描述】:

我无法理解 Node 在并行处理和函数调用返回值方面的操作方式。

仅供参考:下面的 gulp 函数仅作为此问题的示例而创建。

函数是否有可能在Read a large file语句完成处理之前返回流(大文件已从文件系统中完全读取并添加流),或者Node足够聪明以完成所有返回前的声明?

function moveFiles(){

    var gulp = require('gulp'),
        stream = require('merge-stream')();

    // Read a large file
    stream.add(gulp.src('src/large-file.txt')
        .pipe(gulp.dest('dest/'))
    );

    // Read a small file
    stream.add(gulp.src('src/small-file.txt')
        .pipe(gulp.dest('dest/'))
    );

    return (stream.isEmpty() ? null : stream);

}

【问题讨论】:

  • 流就是这样:指向尚未(或仅部分)读取的文件的指针。如果函数已经读取了全部内容,那么 stream 有什么用?
  • 欢迎来到asynchronous programming的精彩世界!
  • @Bergi 是的,异步方面让我很困惑。这个问题与 gulp 本身无关。另一个例子可能是,使用节点:假设同一个函数从文件系统读取两个文件(不使用 gulp 或流,只是简单的节点 fs 调用),然后该函数应该返回连接在一起的两个文件的内容。是否有可能在读取两个文件之前触发 return 语句?
  • 是的,如果您使用异步 fs 方法(不是它们的同步对应方法),通常就是这种情况。而且因为它们在没有完成读取文件的情况下立即返回,您的return 语句将立即得到满足,这也是您的函数还不能返回连接结果的原因。充其量,它可以返回一个代表未来结果的结构。这样的结构可以是一个承诺或一个流。借助此结构,您可以告诉节点在将来有结果时执行某项操作(执行 callback 函数)。
  • Experience, and checking the docs :-) 但是,每个 statement 都是同步评估的。它可能会启动后台任务,但它总是在完全不相关的事情发生之前返回(与多线程环境不同)。在 a(); b(); 中,a 将始终在评估 b 之前返回。

标签: javascript node.js gulp


【解决方案1】:

在完成函数本身的所有操作之前,Node 是否可以从函数调用中返回一个值?

这是一个棘手的问题。答案是否定的,在某种程度上,返回一个值意味着函数已完成执行,它从堆栈中取回并且它永远不会再做任何事情 - 当然除非它在另一个时间被调用,但关键是这个特定的调用结束了。

但棘手的部分是 函数 已完成执行,这并不意味着它不能安排将来​​发生其他事情。稍后会变得更复杂,但首先是一个非常简单的示例。

function x() {
    setTimeout(function () {
        console.log('x1'));
    }, 2000);
    console.log('x2');
    return;
    console.log('x3');
}

在这里,当您调用 x() 时,它会安排 另一个 函数在 2 秒后运行,然后它会打印 x2 然后它会返回 - 此时此函数无法执行任何操作else 再次调用。

这意味着x3 永远不会被打印,但x1 最终会被打印 - 因为它是另一个在超时触发时将被调用的函数。匿名函数将被调用不是因为x() 函数在返回后可以做任何事情,而是因为它设法在返回之前安排了超时时间。

现在,一个函数可以返回一个承诺,该承诺将在一段时间后得到解决,而不是仅仅安排未来发生的事情。例如:

function y() {
    console.log('y1');
    return new Promise(function (resolve, reject) {
        setTimeout(function () {
            resolve('message from y()');
        }, 2000);
    });
    console.log('y2');
}

现在,当你运行时:

var promise = y();

将会发生的事情是y1 将被打印,一个新的承诺将被返回,y2 将永远不会被打印,因为此时y() 将返回并且不能做任何其他事情。但它设法安排了一个超时,将在两秒后解决这个承诺。

您可以通过以下方式观察它:

promise.then(function (value) {
    console.log(value);
});

因此,通过这个示例,您可以看到虽然 y() 函数本身返回并且不能执行任何其他操作,但将来可以调用其他一些(在这种情况下为匿名)函数并完成 y() 函数的工作已启动。

所以我希望现在清楚为什么这是一个棘手的问题。在某种程度上,一个函数在返回后不能做任何事情。但它可以安排一些其他函数作为超时、事件处理程序等,这些函数可以在函数返回后执行某些操作。如果函数返回的东西是一个承诺,那么调用者可以在它准备好时轻松地观察未来的值。

所有示例都可以通过使用箭头函数来简化,但我想明确指出这些都是单独的函数,其中一些是命名的,一些是匿名的。

有关更多详细信息,请参阅其中一些答案:

【讨论】:

  • 感谢您的全面解释。真的很有帮助。所以在我的例子中,使用上面的 gulp 示例,我首先需要了解哪些 Node/Gulp 环境 API/方法实际上是异步/同步的?所以我需要询问以下是否同步:stream.add()gulp.src()
猜你喜欢
  • 2021-08-02
  • 1970-01-01
  • 1970-01-01
  • 2014-08-25
  • 1970-01-01
  • 1970-01-01
  • 2017-05-17
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多