如何利用异步 XMLHttpRequest 的回调函数?

IT技术 javascript asynchronous callback xmlhttprequest
2021-01-19 07:57:50

我目前正在编写 JavaScript 并且对callback感到困惑我发现它不是内置函数……
我现在正在阅读 O'Relly JavaScript 5th Edition,它显示了一个示例代码,如下所示:

getText = function(url, callback) // How can I use this callback?
{
    var request = new XMLHttpRequest();
    request.onreadystatechange = function()
    {
        if (request.readyState == 4 && request.status == 200)
        {
            callback(request.responseText); // Another callback here
        }
    }
    request.open('GET', url);
    request.send();
}

基本上,我想我不明白callback虽然......有人可以写一个示例代码来利用callback上面的优势吗?

5个回答

回调非常简单和漂亮!由于 AJAX 调用的性质,在请求结束之前您不会阻止脚本的执行(那时它将是同步的)。回调只是一种指定的方法,用于在响应返回到您的方法后对其进行处理。

由于 javascript 方法是第一类对象,因此您可以像变量一样传递它们。

所以在你的例子中

getText = function(url, callback) // How can I use this callback?
{
    var request = new XMLHttpRequest();
    request.onreadystatechange = function()
    {
        if (request.readyState == 4 && request.status == 200)
        {
            callback(request.responseText); // Another callback here
        }
    }; 
    request.open('GET', url);
    request.send();
}

function mycallback(data) {
   alert(data);
}

getText('somephpfile.php', mycallback); //passing mycallback as a method

如果您执行上述操作,则意味着您mycallback作为处理响应(回调)的方法传递

编辑

虽然这里的例子没有说明回调的正确好处(毕竟你可以简单地将警报放在 onReadyStateChange 函数中!),但可重用性肯定是一个因素。

你必须记住,这里重要的是 JS 方法是一流的对象。这意味着您可以像对象一样传递它们并将它们附加到各种事件。当事件触发时,会调用附加到这些事件的方法。

当您这样做时,request.onreadystatechange = function(){}您只是分配该方法在适当的事件触发时被调用。

所以这里很酷的事情是这些方法可以重用。假设您有一个错误处理方法,它会在 AJAX 请求中出现 404 的情况下弹出警报并填充 HTML 页面中的某些字段。

如果您无法分配回调或将方法作为参数传递,则必须一遍又一遍地编写错误处理代码,而您只需将其分配为回调,所有错误处理都将被排序一气呵成。


实际上与缩进无关。可重用性当然可以,因为您可以将常用函数作为将被执行的变量传递。我已经扩展了我的答案
2021-03-21 07:57:50
哦,是的,你说得对。可重用性必须是准确的词。谢谢 :)
2021-04-06 07:57:50
这是简单明了的。在这种情况下callback,我认为减少缩进和保持代码可读性是使用函数的目的这是正确的吗?不管怎么说,还是要谢谢你。
2021-04-13 07:57:50

首先,我建议阅读什么是回调。是一个开始。

大图

回调在异步编程中被广泛使用。当您不想阻塞直到(可能)长时间运行的操作完成时,解决问题的一种方法是将操作委托给会在旁边为您完成的人。这就提出了一个问题:您将如何知道操作何时完成,以及您将如何获得其结果?

一种解决方案是将工作委托给其他人,并时不时地从正常工作中抽出一点时间,然后问“我交给你的工作完成了吗?”。如果是这样,请以某种方式获得结果,然后就可以了。问题解决了。

这种方法的问题在于它并没有让你的生活变得更轻松。您现在被迫每隔一段时间询问一次,并且您不会知道操作实际上已经完成(但只有下次您记得询问时)。如果您忘记询问,您将永远不会收到通知。

一个更好的解决方案是回调:在委派工作时,同时提供一个函数。实际执行工作的代码Promise在工作完成后立即调用该函数。您现在可以忘记所有这些事情,并且知道当工作完成时,您的回调将被调用。不早,也不晚。

这里的回调是什么?

在这种特定情况下,callback是您提供的一种功能,getText作为允许它与您进行通信的方式。你实际上是在说“为我做这项工作,当你完成后,这里有一个函数供你调用让我知道”。

getText实际上选择仅在XMLHttpRequest(XHR) 完成时使用此回调,同时它“让您知道”它也将 HTTP 响应的内容传递给您(因此您可以根据该信息采取行动)。

回调和更多回调,天哪!

但请花一点时间阅读代码。它存储的值是request.onreadystatechange多少?目的request.onreadystatechange什么?

答案是request.onreadystatechange您可以使用callback进行填充实际上,XHR 为您提供了一种为其提供回调的方法,并且它Promise在底层 HTTP 请求的状态发生变化时“回电”。

getText是建立一个抽象的函数最重要的是:它可以插入自己的回调(匿名函数-我就指的是作为‘内部’),在那里,并接受另一个从您的回调(参数- I'LL将其称为“外部”)。当内部回调(记住:在状态改变时被调用)检测到状态为“完成”(值的含义4)并且 HTTP 响应状态码为 200(表示“OK”)时,它调用外部回调让您, 的用户getText,知道结果。

我希望我说得有道理。:)

难道你不能只是显式地调用回调方法 onreadystatechange 并且仍然是异步的吗?
2021-03-16 07:57:50
谢谢你的解释!这其实很容易读懂什么是回调,虽然我英文水平不高 :Ponreadystatechange也是回调,事实上,事件驱动编程在我加载很多异步程序时需要深度嵌套处理。在这种情况下,使用callback函数可以减少缩进……我说得对吗?
2021-03-26 07:57:50
@Japboy:这与可重用性无关,尽管如果您在多个场合使用相同的回调,则确实具有可重用性。这是关于实现异步编程。请参阅此处(这是关于异步 I/O,但其中大部分内容适用于任何“工作”,而不仅仅是 I/O):en.wikipedia.org/wiki/Asynchronous_I/O
2021-03-28 07:57:50
@turbo2oh:是的,#2 是可配置的——或者相反,#1 除了一项非常具体的任务外毫无用处。例如,jQuery.get有用只是因为它是可配置的。
2021-03-31 07:57:50
s/减少压痕/保持可重用性/
2021-04-10 07:57:50

我个人更喜欢使用事件侦听器而不是回调。

使用侦听器非常方便,尤其是当您愿意一次处理多个异步请求时。

用法如下(取自https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest

function reqListener () {
  console.log(this.responseText);
}

var oReq = new XMLHttpRequest();
oReq.addEventListener("load", reqListener);
oReq.open("GET", "http://www.example.org/example.txt");
oReq.send()

XMLHttpRequest 回调函数和文件上传数据数组

function HttpPost(url, arr, cb, form){
    if (form === undefined) { var data = new FormData(); }else{ var data = new FormData(form); }
    if (arr !== undefined) {
        for (const index in arr) {    
            data.append(index, arr[index]);
        }
    }
    var hr = new XMLHttpRequest();        
    hr.onreadystatechange=function(){
        if (hr.readyState==4 && hr.status==200){
            if( typeof cb === 'function' ){ cb(hr.responseText); }
        }
    }
    hr.upload.onprogress = function(e) {
        var done = e.position || e.loaded, total = e.totalSize || e.total;
        console.log('xhr.upload progress: ' + done + ' / ' + total + ' = ' + (Math.floor(done/total*1000)/10) + '%');
    };
    hr.open("POST",url,true);
    hr.send(data);
}

// HttpPost callback
function cb_list(res){
    console.log(res);
    var json = JSON.parse(res);
    console.log(json.id + ' ' + json.list);
    // loop
    for (var objindex in json.list){
        console.log(json.list[objindex].id);
    }
}

样本:

var data = [];
data["cmd"] = "get-cos";
var form = $('#form')[0];

HttpPost('/api-load', data, cb_list, form);

<form id="form" method="POST" enctype="multipart/form-data">
    <input type="file" name="file[]" multiple accept="image/*">
</form>

Http头内容

hr.setRequestHeader("Content-Type", "application/json"); 
// data:
var json = {"email": "hey@mail.xx", "password": "101010"}
var data = JSON.stringify(json);

hr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
// data: 
var data = "fname=Henry&lname=Ford";

以适当的“回调”方式工作的是定义一个返回这样的Promise的服务!

$http.head("url2check").then(function () {
                return true;
            }, function () {
                return false;
            });

在控制器中使用服务:

<service>.<service method>.then(function (found)) {
     if (found) {......
}

@jon 将其称为异步是正确的!