如何让回调异步运行?

How to make callback run asynchronously?

提问人:user3425506 提问时间:7/27/2022 更新时间:7/28/2022 访问量:71

问:

我有一个名为getAandB的JavaScript函数,它接受回调。getAandB 首先使用 ajax 获取值 'a'。然后,它调用值为“a”作为参数的回调。回调获取值“b”,并将 console.logs “a”和“b”都记录到控制台。所以我进入了控制台。{"key":"a"} {"key":"b"}

我以为两个ajax调用会同时/异步发生。然而,它们似乎一个接一个地运行,即。同步。

ajax 请求的 JavaScript 代码和 PHP 代码如下所示:

index.html:

<script>
    function getAandB(callback){
        const xhr = new XMLHttpRequest();
        xhr.open('GET', './ajax-a.php', true);
        xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
        xhr.onreadystatechange = function(){
            if(xhr.readyState === 4 && xhr.status === 200){
                callback(xhr.responseText)
            }
        }
        xhr.send();
    }

    function callback(resultA){
        const xhr = new XMLHttpRequest();
        xhr.open('GET', './ajax-b.php', true);
        xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
        xhr.onreadystatechange = function(){
            if(xhr.readyState === 4 && xhr.status === 200){
                const resultB = xhr.responseText;
                console.log(resultA, resultB);
            }
        }
        xhr.send();
    }
    getAandB(callback);
</script>

ajax-a.php:

<?php
sleep(5);
$response = [
    "key" => "a",
];
echo json_encode($response);

ajax-b.php 的代码与 ajax-a 相同.php只是 $response.key 的值是 b 而不是 a。

我认为上面的代码会导致同时进行ajax调用以获取“a”和“b”。但是,如果 ajax-a.php 和 ajax-b.php 的 PHP 代码都休眠了 5 秒,则控制台 .log 需要 10 秒才能出现。如果只有一个ajax-?.PHP 脚本休眠 5 秒,然后需要 5 秒才能显示控制台 .log。

如何使用回调来允许我组合ajax调用的结果,就像我在这里所做的那样,但使单个调用同时/异步发生?或者,是否无法通过回调来实现这一点?

JavaScript Ajax 异步 回调

评论

1赞 IT goldman 7/27/2022
这是因为“会话阻塞”,而不是因为ajax。如果您在没有会话的情况下尝试它,它将并行工作。stackoverflow.com/a/15693029/3807365
1赞 Jamiec 7/27/2022
这与此无关,而与您等待显示第一个呼叫已完成的事实有关,甚至在启动第二个呼叫之前xhr.onreadystatechange
1赞 T.J. Crowder 7/27/2022
你为什么期望XHR呼吁在完成之前发生?在完成之前,你不会打电话。因此,自然而然地,这两个电话接踵而至。(如果您解决了这个问题,并且实际上一个接一个地进行调用,而不等待,您可能会遇到会话阻塞,但现在第二个甚至还没有开始,直到第一个完成。ajax-bajax-acallbackajax-a
0赞 user3425506 7/27/2022
我同意克劳德@T的观点,即我的期望似乎不合逻辑。但是,我对代码的执行了解不够,无法确定。我的问题仍然是我如何使用回调来异步实现这一点,即。在 5 秒内而不是 10 秒?
2赞 IT goldman 7/27/2022
是的,已经确定这不是会话问题。但本来可以

答:

2赞 Quentin 7/27/2022 #1

如果您希望向我发出对 ajax-b 的请求与对 ajax-a 的请求大致同时发出,那么您需要在大约相同的时间进行相应的调用。xhr.send()

目前,对 ajax-b 的调用是作为其中的一部分进行的,只有在收到对 ajax-a 请求的响应才能调用。send()callback()


然后,您需要添加其他逻辑来确定何时收到两个响应,以便同时记录两个数据位(假设您仍然希望这样做)。

一个粗略而现成的方法,保持你目前的方法,看起来像这样:

function getA(callback){
    const xhr = new XMLHttpRequest();
    xhr.open('GET', './ajax-a.php', true);
    xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
    xhr.onreadystatechange = function(){
        if(xhr.readyState === 4 && xhr.status === 200){
            callback(xhr.responseText)
        }
    }
    xhr.send();
}

function getB(callback){
    const xhr = new XMLHttpRequest();
    xhr.open('GET', './ajax-b.php', true);
    xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
    xhr.onreadystatechange = function(){
        if(xhr.readyState === 4 && xhr.status === 200){
            const resultB = xhr.responseText;
            callback(xhr.responseText)
        }
    }
    xhr.send();
}

function getAandB() {
    const data = [];

    function callback(responseData) {
        data.push(responseData);
        if (data.length === 2) {
            console.log(...data);
        }
    }

    getA(callback);
    getB(callback);
}

getAandB();

不过,这些天我们有更好的工具,这要归功于原生支持它们的承诺和现代 API(如 fetch)。

async function getAandB() {
    const dataPromises = [
        fetch("./ajax-a.php").then(r => r.text()),
        fetch("./ajax-b.php").then(r => r.text())
    ];
    const data = await Promise.all(dataPromises);
    console.log(...data);
}
getAandB();

评论

0赞 T.J. Crowder 7/27/2022
不要忘记在调用之前检查 HTTP 失败。:-)text
0赞 user3425506 7/27/2022
@Quentin 我非常感谢您的回复,但需要花更多时间来尝试理解它。在继续使用较新的方法之前,我想先了解一下回调方法。
0赞 user3425506 7/27/2022
@T.J. Crowder不确定你对HTTP失败的评论是针对我还是昆汀,还是两者兼而有之,但不幸的是,我不知道你的意思。
2赞 Ivar 7/27/2022
@user3425506 好在 TJ Crowder 有一个博客,他在其中详细解释了它。;-)
2赞 T.J. Crowder 7/27/2022
@user3425506 - 这是给昆汀的。我说的是事实,它只拒绝它在网络错误时返回的承诺,而不是HTTP错误(如404或500)。您必须检查返回的 Response 对象的 or 属性,才能知道 HTTP 调用是否有效。这是我非常古老的贫血博客上的一篇文章: blog.niftysnippets.org/2018/06/common-fetch-errors.html 编辑:哈哈,伊瓦尔链接了它。:Dfetchokstatus
0赞 user3425506 7/27/2022 #2

我尝试编辑我的问题,但“编辑队列已满”。

我花了一段时间才理解@Quentin的答案,但我终于意识到它依赖于这样一个事实,即回调函数的两个实例化都在更改同一个变量(我认为这是通过引用调用的,并且是数组的默认情况)。鉴于此,尽管实例化彼此一无所知,但可以通过检查数据数组是否更新了两次来了解两个 ajax 调用何时完成。如果有,那么两者都必须已经完成,并且可以安慰数据。

不需要 getAandB 函数。这个更简单、更不容易混淆的代码与 Quentin 的回答完全相同:

    <script>
const data = [];

function getA(){
    const xhr = new XMLHttpRequest();
    xhr.open('GET', './ajax-a.php', true);
    xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
    xhr.onreadystatechange = function(){
        if(xhr.readyState === 4 && xhr.status === 200){
            data.push(xhr.responseText);
            if (data.length === 2){
                console.log(...data);
            }
        }
    }
    xhr.send();
}

function getB(){
    const xhr = new XMLHttpRequest();
    xhr.open('GET', './ajax-b.php', true);
    xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
    xhr.onreadystatechange = function(){
        if(xhr.readyState === 4 && xhr.status === 200){
            data.push(xhr.responseText);
            if (data.length === 2){
                console.log(...data);
            }
        }
    }
    xhr.send();
}

getA();
getB();

    </script>

评论

1赞 Quentin 7/27/2022
回调仍然是回调,即使将其指定为匿名函数表达式。
0赞 user3425506 7/28/2022
@Quentin好吧,我确实明白了,但为什么要不必要地再添加两个呢?保持简单!(除非您有理由分享它)
0赞 Quentin 7/28/2022
我尽可能少地更改您的代码以证明我的观点。
0赞 user3425506 7/28/2022
我接受这一点!谢谢。我会重新接受你的回答