node.js 中的回调是始终异步还是始终同步?或者它们可以是 "sometimes one, sometimes the other"?
Are callbacks in node.js either always asynchronous or always synchronous? Or can they be "sometimes one, sometimes the other"?
我正在尝试在 node.js 中制作一些东西,而且我(和其他开始学习节点的人一样)对它的异步性质有疑问。我搜索了一下,但找不到关于它的具体问题的答案(也许我只是搜索得不是很好......),所以这里是:
node.js 回调,一般来说,保证 是异步的(如果文档这么说的话)吗?如果你自己制作接受回调的函数,你应该以这样的方式设计,使它们始终异步还是始终同步?或者它们可以有时同步,有时不同步吗?
举个例子,假设您想通过 Internet 加载一些数据,并且您创建了一个函数来加载它。但数据不会经常更改,因此您决定将其缓存起来以备将来调用。您可以想象有人会像这样编写该函数:
function getData(callback) {
if(dataIsCached) {
callback(cachedData)
} else {
fetchDataFromNetwork(function (fetchedData) {
dataIsCached = true;
cachedData = fetchedData;
callback(fetchedData);
});
}
}
其中fetchDataFromNetwork是一个异步执行其回调的函数(这有点伪代码,但我希望你明白我的意思)。
这个函数只有在数据没有被缓存时才会异步触发,如果它被缓存了它就直接执行回调。在那种情况下,函数的异步特性毕竟是完全没有必要的。
这种事情是不是气馁了?该函数的第二行是否应该改为 setTimeout(function () {callback(cachedData)}), 0)
以保证它异步触发?
我问的原因是我前一段时间看到一些代码,其中回调内的代码简单地假设回调外的其余函数在回调内的代码之前执行。我有点退缩了,心想 "but how do you know that the callback will fire asynchronously? what if it doesn't need to and executes synchronously? why would you ever assume that every callback is guaranteed to be asynchronous?"
如能对这一点作出任何澄清,我们将不胜感激。谢谢!
你永远不应该有一个接受有时是同步的回调的函数,这可能会导致你 运行 陷入尾递归问题。举个例子:
function doSynchronousWork (cb) {
cb();
}
doSynchronousWork(function looper () {
if (...somecondition...) {
doSynchronousWork(looper);
}
});
在上面的示例中,由于调用堆栈嵌套太深,您将 运行 进入最大调用堆栈超出错误。强制同步函数 运行 通过在继续递归之前清除调用堆栈来异步修复该问题。
function doSynchronousWork (cb) {
process.nextTick(cb);
}
doSynchronousWork(function () {
if (...somecondition...) {
doSynchronousWork();
}
});
注意这个尾递归问题最终会在js引擎中修复
不要混淆回调函数和迭代器函数
您需要摆脱某些东西 不需要异步 的观念 - 在 Node 中,某些东西 需要同步 ,或者它应该是异步的,而不用再想一想。
异步是 Node.js 背后的全部哲学,回调本质上是异步的(如果它们设计得当),这使得 Node 可以像它声称的那样是非阻塞的。这就是回调将异步执行的假设的前提——它是 Node 设计理念的重要组成部分。
Should the second line of the function be setTimeout(function () {callback(cachedData)}), 0) instead, to guarantee that it fires asynchronously?
在 Node 中,我们使用 process.nextTick()
来确保某些东西异步运行,将其功能延迟到事件循环的下一个节拍。
我个人同意您的观点,即从不假设异步代码在函数返回后执行(根据定义,异步只是意味着您不能假设它是同步的,而不是假设它不是同步的)。
但是围绕 javascript 已经形成了一种文化,它认为可以同步或异步的函数是一种反模式。这是有道理的:如果您无法预测 何时 代码运行,则很难对其进行推理。
所以一般来说所有流行的库都会避免它。通常,假设异步代码从不在脚本结束之前运行是非常安全的。
除非您有非常特殊的原因,否则不要编写既可以同步又可以异步的函数 - 它被认为是一种反模式。
你的假设都是正确的。
Are node.js callbacks, in general, guaranteed to be asynchronous if the documentation says so?
是的。当然,还有 functions with async callbacks and functions with sync callbacks,但是 none 两者兼而有之。
If you make your own functions that take callbacks, should you design in such a way so that they're either always asynchronous or always synchronous?
是的。
They could be sometimes synchronous, sometimes not, such as a cache? Is this kind of thing discouraged?
是的。 Very much d̲̭i̫̰̤̠͎͝ͅͅs͙̙̠c̙͖̗̜o͇̮̗̘͈̫ų̗͔̯ŕa҉̗͉͚͈͈̜g͕̳̱̗e҉̟̟̪͖̠̞ͅd͙͈͉̤̞̞̩.
Should the second line of the function be setTimeout(function () {callback(cachedData)}), 0)
instead, to guarantee that it fires asynchronously?
是的,这是个好主意,尽管在节点中您宁愿使用 setImmediate
or process.nextTick
而不是 setTimeout
。
或者使用 promises,它确实保证了异步性,这样你就不必自己关心延迟了。
I saw some code a while back where the code inside the callback simply assumed that the rest of the function outside the callback had executed before the code inside the callback. I recoiled a little at that
嗯,可以理解。即使您使用的 API 保证异步,编写代码仍然会更好,以便可以按执行顺序读取它。如果可能的话,你应该将执行的事情放在异步回调之前(异常证明规则)。
我正在尝试在 node.js 中制作一些东西,而且我(和其他开始学习节点的人一样)对它的异步性质有疑问。我搜索了一下,但找不到关于它的具体问题的答案(也许我只是搜索得不是很好......),所以这里是:
node.js 回调,一般来说,保证 是异步的(如果文档这么说的话)吗?如果你自己制作接受回调的函数,你应该以这样的方式设计,使它们始终异步还是始终同步?或者它们可以有时同步,有时不同步吗?
举个例子,假设您想通过 Internet 加载一些数据,并且您创建了一个函数来加载它。但数据不会经常更改,因此您决定将其缓存起来以备将来调用。您可以想象有人会像这样编写该函数:
function getData(callback) {
if(dataIsCached) {
callback(cachedData)
} else {
fetchDataFromNetwork(function (fetchedData) {
dataIsCached = true;
cachedData = fetchedData;
callback(fetchedData);
});
}
}
其中fetchDataFromNetwork是一个异步执行其回调的函数(这有点伪代码,但我希望你明白我的意思)。
这个函数只有在数据没有被缓存时才会异步触发,如果它被缓存了它就直接执行回调。在那种情况下,函数的异步特性毕竟是完全没有必要的。
这种事情是不是气馁了?该函数的第二行是否应该改为 setTimeout(function () {callback(cachedData)}), 0)
以保证它异步触发?
我问的原因是我前一段时间看到一些代码,其中回调内的代码简单地假设回调外的其余函数在回调内的代码之前执行。我有点退缩了,心想 "but how do you know that the callback will fire asynchronously? what if it doesn't need to and executes synchronously? why would you ever assume that every callback is guaranteed to be asynchronous?"
如能对这一点作出任何澄清,我们将不胜感激。谢谢!
你永远不应该有一个接受有时是同步的回调的函数,这可能会导致你 运行 陷入尾递归问题。举个例子:
function doSynchronousWork (cb) {
cb();
}
doSynchronousWork(function looper () {
if (...somecondition...) {
doSynchronousWork(looper);
}
});
在上面的示例中,由于调用堆栈嵌套太深,您将 运行 进入最大调用堆栈超出错误。强制同步函数 运行 通过在继续递归之前清除调用堆栈来异步修复该问题。
function doSynchronousWork (cb) {
process.nextTick(cb);
}
doSynchronousWork(function () {
if (...somecondition...) {
doSynchronousWork();
}
});
注意这个尾递归问题最终会在js引擎中修复
不要混淆回调函数和迭代器函数
您需要摆脱某些东西 不需要异步 的观念 - 在 Node 中,某些东西 需要同步 ,或者它应该是异步的,而不用再想一想。
异步是 Node.js 背后的全部哲学,回调本质上是异步的(如果它们设计得当),这使得 Node 可以像它声称的那样是非阻塞的。这就是回调将异步执行的假设的前提——它是 Node 设计理念的重要组成部分。
Should the second line of the function be setTimeout(function () {callback(cachedData)}), 0) instead, to guarantee that it fires asynchronously?
在 Node 中,我们使用 process.nextTick()
来确保某些东西异步运行,将其功能延迟到事件循环的下一个节拍。
我个人同意您的观点,即从不假设异步代码在函数返回后执行(根据定义,异步只是意味着您不能假设它是同步的,而不是假设它不是同步的)。
但是围绕 javascript 已经形成了一种文化,它认为可以同步或异步的函数是一种反模式。这是有道理的:如果您无法预测 何时 代码运行,则很难对其进行推理。
所以一般来说所有流行的库都会避免它。通常,假设异步代码从不在脚本结束之前运行是非常安全的。
除非您有非常特殊的原因,否则不要编写既可以同步又可以异步的函数 - 它被认为是一种反模式。
你的假设都是正确的。
Are node.js callbacks, in general, guaranteed to be asynchronous if the documentation says so?
是的。当然,还有 functions with async callbacks and functions with sync callbacks,但是 none 两者兼而有之。
If you make your own functions that take callbacks, should you design in such a way so that they're either always asynchronous or always synchronous?
是的。
They could be sometimes synchronous, sometimes not, such as a cache? Is this kind of thing discouraged?
是的。 Very much d̲̭i̫̰̤̠͎͝ͅͅs͙̙̠c̙͖̗̜o͇̮̗̘͈̫ų̗͔̯ŕa҉̗͉͚͈͈̜g͕̳̱̗e҉̟̟̪͖̠̞ͅd͙͈͉̤̞̞̩.
Should the second line of the function be
setTimeout(function () {callback(cachedData)}), 0)
instead, to guarantee that it fires asynchronously?
是的,这是个好主意,尽管在节点中您宁愿使用 setImmediate
or process.nextTick
而不是 setTimeout
。
或者使用 promises,它确实保证了异步性,这样你就不必自己关心延迟了。
I saw some code a while back where the code inside the callback simply assumed that the rest of the function outside the callback had executed before the code inside the callback. I recoiled a little at that
嗯,可以理解。即使您使用的 API 保证异步,编写代码仍然会更好,以便可以按执行顺序读取它。如果可能的话,你应该将执行的事情放在异步回调之前(异常证明规则)。