简体   繁体   English

如何延迟javascript的执行直到加载JSON

[英]How to delay execution of javascript function until JSON has loaded

I have a page that chains two API calls, loads the data into first_data and second_data before executing a createPage function (which is several kb of data manipulation and d3.js): 我的页面链接了两个API调用,并在执行createPage函数之前将数据加载到first_datasecond_data (这是几KB的数据操作和d3.js):

template.html template.html

<script src="createPage.js"></script>
<script>
var first_data, second_data = [], [];

function getFirstData(){
    return new Promise(function(resolve) {
        var xhr = new XMLHttpRequest();
        var url = "/API/my-request?format=json"
        xhr.onreadystatechange = function() {
            if (xhr.readyState == 4 && xhr.status == 200) {
                first_data = JSON.parse(xhr.responseText);
                resolve('1');
            }
        }
        xhr.open("GET", url, true);
        xhr.send();
    });
} //similar function for getSecondData()

getFirstData()
    .then(getSecondData)
    .then(createPage(first_data, second_data));
</script>

The trouble is that some of the code that manipulates the data in createPage is showing errors, for example " can't convert undefined to object ". 问题在于,一些操作createPage数据的代码显示错误,例如“ can't convert undefined to object ”。 In that particular error's case, it's because I try to do Object.keys(data[0]) on some data that should be loaded from the API requests. 在这种特定错误的情况下,这是因为我尝试对应该从API请求加载的某些数据执行Object.keys(data[0]) Some observations: 一些观察:

  • If I inspect the data in the browser dev console, it's all there. 如果我在浏览器开发者控制台中检查数据,就可以了。
  • If I just paste the code from the file in the console, the page draws fine. 如果我只是将文件中的代码粘贴到控制台中,则页面绘制会很好。
  • If I hard-code the initializing arrays etc for the data manipulation part of the code (to get rid of the can't convert undefined , then the page draws but all the graphics indicate that they were populated with no data. 如果我将代码的数据处理部分的初始化数组等硬编码(以摆脱can't convert undefined ,那么该页面会绘制,但是所有图形均表明它们中没有数据。
  • The page loads fine if I put the the JSON data in a .js file and load it as a script just before the createPage.js file at the end of the body. 如果将JSON数据放入.js文件并将其作为脚本加载到正文末尾的createPage.js文件之前,则页面加载正常。
  • I inserted a console.log("starting") statement at the start and end of createPage() . 我在createPage()的开始和结尾处插入了console.log("starting")语句。 Looking at the network and js console output when I load, the starting output occurs before the two API GET requests are displayed in the network activity. 查看我加载时的网络和js控制台输出, starting输出发生在网络活动中显示两个API GET请求之前。 Is this representative of what's really happening (ie can you mix javascript console and network console timing?) 这代表了实际发生的事情吗(即,您可以混合使用JavaScript控制台和网络控制台计时吗?)

So, clearly I don't have access to the data at the point when I need it. 因此,很明显,在需要时我无法访问数据。

  • Why? 为什么? Are my Promises incorrect? 我的Promises不正确吗?
  • How can I fix this? 我怎样才能解决这个问题?

Promise.prototype.then() expects 2 arguments( onFulfilled & onRejected ) as function-expression(OR handler or callback) as it is a function(handler) which will be invoked when Promise is fulfilled Promise.prototype.then()期望有2个参数( onFulfilled & onRejected )作为function-expression(OR handler or callback)因为它是一个函数(处理程序),当Promise实现时将被调用

In your case, createPage(first_data, second_data) will invoke the function createPage when statement is interpreted by interpreter. 在您的情况下,当解释器解释语句时, createPage(first_data, second_data)将调用函数createPage

Use anonymous function as an argument and invoke your function inside it. 使用anonymous function作为参数并在其中调用您的函数。

getFirstData()
  .then(getSecondData)
  .then(function() {
    createPage(first_data, second_data);
  });

Edit: If you are not passing any arguments specifically to the callback, you can use .then(FUNCTION_NAME) 编辑:如果您没有将任何参数专门传递给回调,则可以使用.then(FUNCTION_NAME)

In functional programming and using promises, you should probably refactor getFirstData (and getSecondData) to the following form: 在函数式编程和使用Promise时,您可能应该将getFirstData(和getSecondData)重构为以下形式:

function getFirstData(){
    return new Promise(function(resolve, reject) {
        var xhr = new XMLHttpRequest();
        var url = "/API/my-request?format=json"
        xhr.onreadystatechange = function() {
            if (xhr.readyState == 4 && xhr.status == 200) {
                // Resolve the result, don't assign it elsewhere
                resolve(JSON.parse(xhr.responseText));
            } else {
                // Add rejection state, don't keep the promise waiting
                reject("XHR Error, status = ", xhr.status); 
            }
        }
        xhr.open("GET", url, true);
        xhr.send();
    });
}

And then resolve the promises like this (assume first & second data is not dependant on each other) 然后像这样解决承诺(假设第一和第二数据互不依赖)

Promise.all([getFirstData(), getSecondData()]).then(function(allData){
  var first_data = allData[0];
  var second_data= allData[1];
  return createPage(first_data, second_data);
}).catch(function(error){
  console.log("Error caught: ", error);
});

To make things even cleaner, you can change createPages's from: 为了使事情更加整洁,您可以从以下位置更改createPages:

function createPage(first_data, second_data){
  // Function body
}

to

function createPage(data){
  var first_data = data[0];
  var second_data= data[1];
  // Function body
}

and the Promise part: 和Promise部分:

Promise.all([getFirstData(), getSecondData()]).then(createPage);

Notice how short it became? 请注意它变短了吗?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM