连接来自JavaScript XMLHttpRequest的多个响应

时间:2018-07-28 20:33:55

标签: javascript scope xmlhttprequest string-concatenation

我有来自两个不同URL的HTML内容,需要将它们附加到一个HTML元素上。

我正在使用JavaScript ES5创建一个字符串变量,并简单地将两个HTTP请求的结果串联起来。

在完成请求后,我希望此变量具有以下字符串值:

<script src='foo/bar/script.js'></script><div>content</div>

然后,我想获取该变量并将其(使用jQuery)附加到页面中的HTML元素中。最终目标是用两个HTTP请求的结果填充div,如下所示:

<div class="target-element">
    <script src='foo/bar/script.js'></script><div>content</div>
</div>

我在代码中无法理解的原因是为什么storeHttpResult中的console.log反映了我期望的combinedHttpResult变量,但是在getContent中, combinedHttpResult未被修改。

var libsLocation = 'http://some/location/libs.html'; //contents: <script src='foo/bar/script.js'>
var htmlLocation = 'http://some/location/snippet.html'; //contents: <div>content</div>
var combinedHttpResult = '';

// taken from David Flanagan JavaScript book, p499:
function getText(url, callback) { 
    var request = new XMLHttpRequest();         
    request.open('GET', url);                   
    request.onreadystatechange = function() {   
        if (request.readyState === 4 && request.status === 200) {
            var type = request.getResponseHeader("Content-Type");
            if (type.match(/^text/))            // make sure the response is text
                callback(request.responseText); // pass it to the callback
        }
    };
    request.send(null);
}
function storeHttpResult(e) {   
    console.log('before: ' + combinedHttpResult);
    combinedHttpResult += e;   
    console.log('after: ' + combinedHttpResult);
}
function getContent(libLoc, htmlLoc) {
    console.log('**** ' + combinedHttpResult);
    if ($('.target-element').length > 0 ) {
        $('.target-element').each(function() {
            getText(libLoc, storeHttpResult);
            getText(htmlLoc, storeHttpResult);

            $(this).append(combinedHttpResult); // empty string - why?
        });
    }
    console.log('~~~~ ' + combinedHttpResult);
}
getContent(libsLocation, htmlLocation);

1 个答案:

答案 0 :(得分:1)

这是比赛条件的问题。为了清楚地了解问题的本质,您必须了解javascript中“ Event Loop”的工作原理。

长话短说(希望您可以观看该视频)是因为您的代码未按照编写的顺序执行。

您期望的是,当您调用getText()时,它将调用所有基础函数,但是此处的执行流程有点不同步。调用getText(libLoc,storeHttpResult);

  • storeHttpResult被调用
  • 创建XMLHttpRequest的实例
  • 已注册XMLHttpRequest事件“ onreadystatechange”的处理程序
  • getText(htmlLoc,storeHttpResult)被调用

请注意,最后一步是调用getContent上的下一个表达式。当请求完成后,storeHttpResult将被执行,并且您无法预测何时发生。

在这种情况下,此类异步解决方案。操作已创建。简单的是Promises。这取决于您必须支持哪种浏览器,但是如果它恰好是现代的,那么我强烈建议您使用Promises。

使用承诺解决方案可能看起来像这样:

function getText(url) { 
    return new Promise(function(resolve){
      var request = new XMLHttpRequest();         
      request.open('GET', url);                   
      request.onreadystatechange = function() {   
        if (request.readyState === 4 && request.status === 200) {
            var type = request.getResponseHeader("Content-Type");
            if (type.match(/^text/))            // make sure the response is text
                resolve(request.responseText); // pass it to the callback
        }
      };
      request.send(null);
    });
}

function getContent(libLoc, htmlLoc) {
    console.log('**** ' + combinedHttpResult);
    if ($('.target-element').length > 0 ) {
        $('.target-element').each(function() {
            var self = this;
            Promise.all([getText(libLoc), getText(htmlLoc)]).then(function(response){
              $(self).append(response[0]);
              $(self).append(response[1]);
            });
        });
    }
    console.log('~~~~ ' + combinedHttpResult);
}
getContent(libsLocation, htmlLocation);

请注意,此代码可以改善某些方面,但是了解如何处理Promises是一个很好的开始。

如果您想深入探讨该主题,还可以使用其他解决方案,例如GeneratorsObservables