我试图理解在闭包中包含HTTP调用的NodeJS函数中如何处理近并发请求。
在下面的示例中, sayHello 向另一个服务发出HTTP请求。
如果对函数进行了两次近并发调用(1:name ='Fred'和2:name ='Jane'),第3行的重新分配是否可能导致name ='jane'两个电话都被退回了?
由于第3行是在HTTP事件返回后的后续滴答中处理的闭包中,它是否可以从第1行的父作用域引用?
1 function (req, name, done) {
2 service.user.sayHello(name, function (err, status, body) {
3 name = name.toLowerCase();
4 return done(null, {firstName: name});
5 });
6 }
答案 0 :(得分:2)
对函数的每次调用都会创建一组全新的局部变量和函数参数变量,因此对该函数的一次调用与下次再次调用同一函数完全无关。
你必须要注意的一件事是任何引用在函数之外定义的变量,但函数内部定义的任何东西都是其范围所独有的,并且对于函数的执行是唯一的。
所以,在这个回调中:
1 function (req, name, done) {
2 service.user.sayHello(name, function (err, status, body) {
3 name = name.toLowerCase();
4 return done(null, {firstName: name});
5 });
6 }
每次此功能时,变量req
,name
,done
,err
,status
和body
都是独立且唯一的变量叫做。正如您使用name = name.toLowerCase()
一样分配其中一个只会影响此版本的name
变量,而不会影响通过再次调用此函数创建的任何其他版本。
如果对该功能进行了两次近乎同时的通话(1:名称='弗雷德'以及2:名称='简'),是否可以重新分配第3行可能会导致姓名=&jans'两个电话都被退回了?
没有。函数的参数是唯一的,并且每次调用函数时都是独立的,并且一个函数调用的参数与其他函数无关。
由于第3行是在HTTP事件返回后的后续滴答中处理的闭包中,它是否可以更改第1行的父作用域的引用?
不,除非您在此功能中的代码更改了它。没有外部代码可以访问它。
还有一件事需要注意(在这个特定的例子中似乎没有发挥作用)。将对象传递给函数时,该对象将作为指针传递。这意味着如果在您运行异步操作时任何其他代码也可能正在修改该对象,那么该对象可能会从您下面修改出来。这是一个简单的例子:
function delayIncrement(obj, t) {
console.log(`${t} timer started, obj.cntr = ${obj.cntr} at entry`)
setTimeout(function() {
console.log(`${t} timer fired, obj.cntr = ${obj.cntr} before incr`);
++obj.cntr;
}, t);
}
let sampleObj = {cntr: 1};
delayIncrement(sampleObj, 100);
delayIncrement(sampleObj, 50);
delayIncrement(sampleObj, 10);
// now example sampleObj a long time later after all timers have completed
setTimeout(function() {
console.log(`final result for sampleObj.cntr = ${sampleObj.cntr}`);
}, 1000);

在这个例子中,sampleObj被传递给每个delayIncrement()
调用,因为它是一个对象,它由指针传递。这意味着当任何delayIncrement()
函数调用修改sampleObj
的任何属性时,它作为指针传递的所有函数都将看到相同的修改。
这种在Javascript中通过指针传递的概念仅在传递对象时发生。当您传递字符串或数字或布尔值或Javascript引用的任何类型作为基元时,它将通过值传递,而不是通过指针传递。在您的示例中,我假设name
是一个字符串,因此它不受对象所在的问题的影响。请记住,{name: "Bob"}
不是Javsacript中唯一的对象类型。数组也是一个对象,如Map
或Set
以及许多其他类似的对象类型。 Javascript原始类型包括Boolean
,Null
,Undefined
,Number
,String
,Symbol
。更多详情here on MDN。
答案 1 :(得分:1)
您只传递一个参数' name'对于第1行的函数,因此每个函数调用都有一个名称变量作为参数传递,因此您引用的是name参数而不是名称全局对象,每个函数调用都会在堆栈队列中拥有它自己的作用域。它们几乎同时发生的事实没有区别,它们在你编写的函数中仍有自己的范围。参数'名称'仅存在于函数调用中。因此,如果进行两个不同的函数调用,则参数名称将创建两次,并且仅存在于调用范围内。所以名字==='弗雷德'拥有自己的范围和名称==='简'它在堆栈队列中拥有自己的作用域。 所以不,名字='弗雷德'永远不会是名字='简'。