承诺是否异步或同步解决

Do promises resolve asynchronously or synchronously?

本文关键字:同步 解决 异步 是否 承诺      更新时间:2023-09-26

我最近一直在使用 JavaScript 承诺,遇到了以下情况,这让我思考:

var combinedArray = [];
function getArrayOne() {
    $http.post(arrayOnePath).then(function(arr) {
        combinedArray = combinedArray.concat(arr);
    }) // More code preventing me from using Promise.all(...)
}
function getArrayTwo() {
    $http.post(arrayTwoPath).then(function(arr) {
        combinedArray = combinedArray.concat(arr);
    }) // More code preventing me from using Promise.all(...)    
}
function getAllArrays() {
    getArrayOne();
    getArrayTwo();
}

当我编写这个逻辑时,我突然意识到,如果两个承诺同时解决(当它们访问共享资源时),则可能存在潜在的竞争条件。在考虑了一段时间后,我意识到then(..)决议是在帖子返回后执行的,这意味着这段代码在 JavaScript 的同步执行环境中运行。

有人可以向我澄清一下,如果两个承诺同时解决,这两个combinedArray.concat(arr);声明是否会引起问题?

[编辑]在评论之后,我只想补充一点,我不介意数组连接成combinedArray的顺序。

JavaScript 是单线程的,即使在运行异步调用时也能防止竞争情况。

在某些情况下,JS将在后台使用另一个线程,例如节点的I/O函数,并且Web worker API允许您生成一个隔离但独立的线程(没有内存访问,但它们可以传递消息)。

因为JS最初是单线程的,运行时中的所有内容都依赖于此(旧代码假设它),所以他们不能只添加多线程和潜在的竞争条件。它会破坏一切。因此,此代码将始终正确安全地工作,因为承诺将被添加到单个队列中并一个接一个地解析。

即使在 Web Worker 中(以及等效的节点),每个"线程"都有一个隔离的内存空间,并且不能直接从另一个线程访问变量。Web 工作者专门使用 postMessage 方法来序列化对象,并以安全的方式将它们发送到另一个线程。

关于传递到 Promise API 的函数需要了解的事情:

  • 传递给承诺执行器 (new Promise(fn)) 的函数fn会立即执行。

  • 传递给处理程序(.then(fn))的函数fn是异步执行的。

在 JavaScript 环境中,没有两个函数会同时执行,除非你使用的是 Web Workers(谢谢@zzzzBov)。无论哪种方式,这都不是异步的含义或暗示。

您的示例中没有争用条件,因为争用条件决定了实现存在问题。因此,尽管您无法预测哪些函数将在另一个函数之前执行,但这两种结果都不会对程序的操作产生不利影响。当然,除非你的程序依赖于首先执行的串联操作之一......(我可以看到它没有,从你的编辑)。


"竞争条件"维基百科:

竞争

条件或竞争危险是电子、软件或其他系统的行为,其中输出取决于其他不可控事件的顺序或时间。当事件没有按照程序员预期的顺序发生时,它就会成为一个错误。

正如我已经解释过的,你的用户级Javascript是单线程的(除了webWorkers,这里不涉及)。

没有两个Javascript片段在同一时刻运行,所以你没有竞争条件。 您的特定代码示例确实存在一个问题,不知道何时完成两个异步操作,以便您可以使用结果。 因此,您可以使用如下结构:

function getArrayOne() {
    return $http.post(arrayOnePath).then(function(arr) {
        // do any processing here on or with arr
        return arr;
    })
}
function getArrayTwo() {
    return $http.post(arrayTwoPath).then(function(arr) {
        // do any processing here on or with arr
        return arr;
    })
}
function getAllArrays() {
    return Promise.all(getArrayOne(), getArrayTwo()).then(function(results) {
        // results[0] is first array
        // results[1] is second array
        return results[0].concat(results[1]);
    });
}
getAllArrays().then(function(results) {
     // all results available
}, function(err) {
     // error occurred here in either async operation
});

这不仅会告诉您所有异步操作何时完成并为您提供组合结果,而且它们按顺序排列,并且会传播任一操作的错误。