【问题标题】:How to use break to come out of while loop based on xmlhttp response code?如何根据xmlhttp响应代码使用break退出while循环?
【发布时间】:2017-07-29 11:17:27
【问题描述】:
    while (repoUrlLink != null && count < 90) {
        var xmlHttp = new XMLHttpRequest();
        xmlHttp.open('GET', repoUrlLink, false);
        xmlHttp.setRequestHeader('Authorization', 'Bearer ' + userData.accessToken);
        xmlHttp.onload = function () {
            if (xmlHttp.status != 200) {
                displayNoAccessMessage();
                break;
            }
            var result = JSON.parse(xmlHttp.responseText);
            if (result.length == 0) {
                displayNoRecordsMessage(); 
                break;                
            }
            var header = xmlHttp.getResponseHeader('link');
            if (header) {
                //doing something
            }
            else
                repoUrlLink = null;

            $.each(result, function (index, eachData) {
                // doing something with data
                count++;
            });
        }
        xmlHttp.send();
    }

一旦我显​​示错误,有没有更好的方法可以退出循环。break 语句不起作用。有什么回调有用吗?

【问题讨论】:

  • 不可能。在循环完成之前,回调永远不会运行。你不能使用循环,使用递归方法。
  • @Bergi 您能否提供修改后的示例?也只是想确认这种方法是否好,或者有没有更好的方法来做同样的事情?
  • while 循环的同步break 语句放在异步回调函数代码中不起作用。控制台上的 JavaScript 编译器错误消息抱怨它。有关如何处理异步结果的深入讨论,请参阅 stackoverflow.com/q/14220321/5217142。我建议创建一个函数,该函数返回一个请求操作的 Promise,该操作在错误时被拒绝,并用于实现异步递归以进行重试。
  • 这有助于将获取数据、识别和响应错误以及处理数据的操作分开。目前它们都混在一起导致代码失败。
  • @Traktor53 能否请您提供代码的架构/布局?那真的很有帮助。

标签: javascript asynchronous callback xmlhttprequest


【解决方案1】:

正如有人在 cmets 中所说,您需要使用递归。这是因为 xmlHttp 是一个异步操作。当您调用send 时,浏览器会发送请求,然后继续执行代码,因此在调用 onload 函数时,跳出循环不再有效。此外,作为一个函数,onload 无法调用 break,因为 while 循环甚至不在同一范围内。

var count = 0;
var doXmlHttpCall = function() {

    var xmlHttp = new XMLHttpRequest();
    // additional setup code here
    xmlHttp.onload = function() {
        var errorFound = false;

        if (/* some error case */) {
            errorFound = true;
        }

        // process request

        if (count < 90 && !errorFound) {
            doXmlHttpCall();
        }
    }
}

doXmlHttpCall();

【讨论】:

    【解决方案2】:

    使用 Promise 重构代码的一些想法。

    • 一个承诺的 XMLHttpRequest 请求函数getRepLink,它执行一个请求。它拒绝请求错误和 HTTP 错误(不是“200”状态)。

    • 一个承诺的getSetOfRecords 函数,用于获取单个响应,将其解析为 JSON 数据并提取链接头值。如果没有记录,则拒绝。

    • 一个承诺的进程记录函数,它尝试处理集合中给定数量的记录。它满足处理的记录数。如果某些记录已被处理,它会忽略无记录错误。

     // XMLHttp request
    
    function getRepoLink (repUrlLink) {
        return new Promise( function (resolve, reject) {
            var xmlHttp = new XMLHttpRequest();
            xmlHttp.open('GET', repoUrlLink, false);
            xmlHttp.setRequestHeader('Authorization', 'Bearer ' + userData.accessToken);
            xmlHttp.onload = function () {
                if( xmlHttp.status == 200) {
                    resolve( xmlHttp);
                }
                else {
                    reject( new Error("HTTP error " + xmlHttp.status));
                }
            };
            xmlHttp.onerror = reject;
            xmlHttp.send();
        });
    }
    
    
    // get a set of records
    
    const EndOfRecords = new Error( "End of Records");
    
    function getSetOfRecords( repUrlLink) {
        return getRepoLink( repUrlLink).then(
            function( xmlHttp) {
                var result = JSON.parse(xmlHttp.responseText);
                if (result.length == 0) {
                     displayNoRecordsMessage();
                     throw EndOfRecords;  // reject the returned promise
                }
                var header = xmlHttp.getResponseHeader('link');
                return {result, header};  // fulfill the returned promise
            }
        );
    }
    
    // get up to `count` records and process them
    
     function processRecords( repUrlLink, count) {
        var processed = 0;
        function processSomeMore() {
            return getSetOfRecords().then( function ({result, header}) {
                $.each(result, function (index, eachData) {
                  // do something with data
                  processed++;
                });
                if( header) {
                     //do something with header
                     if( processed < count)
                         return processSomeMore() // asynchronous "recursion"
                }
                else {
                    // do as required if no link header present.
                }
                return processed;  // fulfill returned promise
            },
            function( error) {
                if( error === EndOfRecords && processed > 0) {
                    return processed; // got some records
                };
                throw error;  // reject returned promise
            });
        }
        return processSomeMore();
    }
    
    //  Start asynchronous operation
    
    processRecords( repUrlLink, 90)
    .then( processed => console.log( processed + " records processed"))
    .catch( error => console.log( error));
    

    【讨论】:

    • 有人建议我使用 jquery ajax 调用而不是 xmlhttp 作为更清洁的方式。您对此有何看法?
    • $ajax 很好 - 节省了调试和验证代码,无论如何您都在使用 Jquery,并且它已记录在案。但要小心将 JQuery 版本 3 之前的 $ajax 返回的 jqXHR 对象视为承诺对象 - 它们不完全符合承诺规范。
    • @Tracktor53 您能否提供我的示例代码的任何结构布局与 ajax。实际上我是 javascript/ajax 的新手,所以我很难将这些部分组合在一起。
    猜你喜欢
    • 2012-12-05
    • 2020-03-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-07-12
    • 2015-12-06
    • 2018-06-13
    • 2023-04-08
    相关资源
    最近更新 更多