Javascript 下面有什么比赛条件吗?

Javascript 下面有什么比赛条件吗?,javascript,node.js,express,race-condition,Javascript,Node.js,Express,Race Condition,我的express服务器中有以下代码(为了简洁起见,已将其删减)。我在三个不同的restful端点中添加/修改/读取一个公共对象。由于NodeJ中的所有http请求都是异步的,所以我可以同时获取put和get请求。所以,虽然PUT发生了,但假设状态没有更新,我的GET可能会得到一个稍微陈旧的响应 据我所知,我的测试表明这里没有比赛条件。因为更新结果对象是一个同步操作,所有异步操作都应该等待它。有人能帮我更好地解释一下这是否正确吗 var obj = {}; const exec

我的express服务器中有以下代码(为了简洁起见,已将其删减)。我在三个不同的restful端点中添加/修改/读取一个公共对象。由于NodeJ中的所有http请求都是异步的,所以我可以同时获取put和get请求。所以,虽然PUT发生了,但假设状态没有更新,我的GET可能会得到一个稍微陈旧的响应

据我所知,我的测试表明这里没有比赛条件。因为更新
结果
对象是一个同步操作,所有异步操作都应该等待它。有人能帮我更好地解释一下这是否正确吗

    var obj = {};
    const exec = require('child_process').exec;
    app.post('/foo', (req, res) => {
         var result = {};
         result.id = generateSomeRandomId();
         result.failed = 0;
         result.status = 'running'
         //execute some command and update result
         const child = exec('some command');
         child.stdout.on('data',  (data) => {
             //some logic
         });
         child.stderr.on('data',  (data) => {
             result.failed = result.failed + 1;
          });
         child.on('close',  (code, signal) => {
              if (signal !== null && signal !== undefined) {
                    result.status = 'cancelled';
              } else {
                    result.status = 'completed';
                    result.runtime = calculateRunTime();
                }
         });
         result.pid = child.pid;
         obj[result.id] = result; 
         res.send(result); 
    }   

    app.put('/foo/:id', (req, res) => {
         var result =  obj[req.params.id];
         if (result.status === 'running' && result.pid !== undefined) {
              kill(result.pid, 'SIGKILL');
              result.status = 'cancelled';
              result.runtime = calculateRunTime();
         }
         res.send(result);
    }   
    app.get('/foo/:id', (req, res) => {
         var result =  obj[req.params.id];
         res.send(result);
    }

这只是一个想法,但也许承诺在这里会有所帮助:

var obj = {};
const exec = require('child_process').exec;
app.post('/foo', (req, res) => {
     var result = {};
     result.id = generateSomeRandomId();
     result.status = 'running';
     const child = exec('some command');
     child.stdout.on('data',  (data) => {
         //some logic
     });

     result.promise = new Promise(resolve => {
       child.stderr.on('data',  (data) => {
           result.failed = result.failed + 1;
           resolve(false);
        });
       child.on('close',  (code, signal) => {
            // ...
           resolve(true);
       });
     });

     result.pid = child.pid;
     obj[result.id] = result;
     res.send(result); 
}   

app.get('/foo/:id', (req, res) => {
     var result =  obj[req.params.id];
     if(result.status === 'running') {
       result.promise.then(() => res.send(result));
     }
     else {
       res.send(result);
     }
}

在这种情况下,GET将仅在子项由错误或“关闭”事件完成时响应。

您没有任何我称之为“竞争条件”的内容;这里有一个不确定因素在起作用,但在实践中这不太可能重要

它看起来像是您的
post
启动一个进程并返回ID,您的
put
取消进程,您的
get
返回进程的当前状态。由此我推断,在您的
post
完成并提供ID之前,您永远无法
get

如果在
exec
异步侦听器完成之前进行
get
调用,则会收到并返回该调用​, 你会得到任何最后一个进行中的状态-我想这是设计的。因此,这里唯一可能的冲突是,如果您发出了一个
put
调用来停止您的进程

在与结果对象交互时,您的
put
get
都是同步的,因此,无论哪一个先收到,都将首先完成。出于我们的目的,您可以忽略进程取消请求,因为它不会修改结果对象。无法保证它们将按照客户发送的相同顺序接收,这在您的场景中可能是一个实际问题,也可能不是一个实际问题

我相信(虽然我的内存可能有问题),如果您使用
集群
来处理不同进程中的请求,那么无论如何都无法通过共享对象传递数据,因此已经排除了这种可能性增加的任何复杂性


因此,网络性能和可靠性的差异是这里唯一真正的通配符。服务器将按照请求进入的顺序处理请求,并提供预期的结果。如果您只有一个客户机,则可以等到收到前一个请求的响应后再发送下一个,这可能会降低您的性能,但会让它或多或少地防弹。否则,只需发送您的请求,不要担心,只要让您的应用程序足够健壮,即使您已经取消了流程,也可以识别并处理第二个取消请求。

您是在
app.post('/foo'
)中谈论的吗?没有“竞赛”,但您将
res.post(结果)
在任何异步部分运行之前……因此,无论您在
//执行一些命令并更新结果
result.pid=child.pid之间做什么;
都不会影响您在
res.send(result)中发送的内容
另外,
result.failed=result.failed+1;
会导致
结果。失败的
未定义的
或者,如果有任何错误,
result.failed==NaN
@JaromandaX是的,我得到了那部分。post中的所有异步操作都将等待所有同步操作完成。我的问题主要是关于并发PUT和get-re任务。我会有比赛条件吗。你对结果的看法是对的。失败的
NaN
部分,为了简洁起见,我跳过了初始化。我会把它添加回去。我只是重新阅读了代码…我知道你在做什么,所以,你开始“一些命令”然后投票选出result@JaromandaX你能告诉我你对竞争条件的看法吗?+1,谢谢。使用承诺是一个有趣的想法。我的要求是,即使子进程正在运行,我也需要返回当前结果,所以我不想等到子进程完成。我还有一个PUT,它也可以修改es结果。在这个解决方案中,如果未完成子项,会发生什么情况?客户端等待?@Manali是的,它将等待。您认为原始代码中存在竞态条件吗?您能解释一下吗?@Manali是的,我认为可能存在某种竞态条件。例如,您发送了两个请求,一个接一个地PUT和GET。您期望的是PUT将终止进程,GET将使您处于“已取消”状态。但在通过网络发送这些请求时,包的顺序可能会更改,GET可能在PUT之前出现(此外,PUT请求可能会丢失)。你会得到不相关的图片。谢谢Jason,这是有道理的。正如你所说,POST和get之间没有竞争条件,无论对象的当前状态是什么,get都会返回。现在问题来了,POST和PUT同时进行,如果POST“几乎”完成(刚进入
child.on('close')
)同时,PUT也来了,我可能会取消这个过程,尽管它已经合法地完成了执行。承诺能更好地避免这种情况吗?如果不理解您的要求,我无法回答这个问题-是否存在“无效”取消进程的时间到了?如果是这样,那么需要在结果对象中跟踪该条件,您需要检查它并选择“失败”取消-您可以通过更改
result.status='finishing'
来完成此操作,只要
child.on('close')
激发,这将导致其无法通过您的
结果。状态==“正在运行”
签入您的put。如果没有无效的时间来取消进程,那么您只需接受即使合法完成也可以取消