【问题标题】:How to delay execution of javascript function until JSON has loaded如何延迟javascript函数的执行直到JSON加载
【发布时间】:2016-04-07 16:32:41
【问题描述】:

我有一个链接两个 API 调用的页面,在执行 createPage 函数之前将数据加载到 first_datasecond_data(这是几 kb 的数据操作和 d3.js):

模板.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>

问题在于处理createPage 中数据的一些代码显示错误,例如“can't convert undefined to object”。在那个特定错误的情况下,这是因为我尝试对一些应该从 API 请求加载的数据执行Object.keys(data[0])。一些观察:

  • 如果我在浏览器开发控制台中检查数据,它就在那里。
  • 如果我只是将文件中的代码粘贴到控制台中,页面绘制得很好。
  • 如果我为代码的数据操作部分硬编码初始化数组等(以摆脱 can't convert undefined,然后页面绘制,但所有图形都表明它们没有填充数据。
  • 如果我将 JSON 数据放在 .js 文件中,并将其作为脚本加载到正文末尾的 createPage.js 文件之前,则页面加载正常。
  • 我在createPage() 的开头和结尾插入了一个console.log("starting") 语句。查看我加载时的网络和 js 控制台输出,starting 输出发生在网络活动中显示两个 API GET 请求之前。这是否代表了真正发生的事情(即您可以混合使用 javascript 控制台和网络控制台计时吗?)

所以,显然我无法在需要时访问数据。

  • 为什么?我的Promises 不正确吗?
  • 我该如何解决这个问题?

【问题讨论】:

  • 你正在调用createPage(first_data, second_data),-() 将调用该函数!可能是function(){ createPage(first_data, second_data); }
  • 好的,这就是问题所在。我非常感谢对为什么.then(createPage()) 不会延迟调用函数到承诺解决时的更长解释——我很讨厌函数式编程。而且,这是否意味着我也应该.then(function(){getSecomdData()})
  • 补充 Rayon 的答案:您还可以将 Promise 作为 .then 的参数。但是是的 - 经验法则 - 总是有 .then(someFunction).then(somePromise)

标签: javascript json ajax promise


【解决方案1】:

Promise.prototype.then() 需要 2 个参数(onFulfilled &amp; onRejected)作为 function-expression(OR handler or callback),因为它是一个函数(处理程序),将在 Promise 实现时调用

在您的情况下,createPage(first_data, second_data) 将在解释器解释语句时调用函数createPage

使用anonymous function 作为参数并在其中调用您的函数。

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

编辑:如果你没有专门向回调传递任何参数,你可以使用.then(FUNCTION_NAME)

【讨论】:

  • 好吧,在这个阶段我必须接受这个答案,因为它确实有效,尽管我更喜欢 kazenorin 的重构代码。我只是想不通为什么当它在原始分解中工作正常时,它无法在其重构配置中成功执行 ajax 函数。
【解决方案2】:

在函数式编程和使用 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();
    });
}

然后像这样解决承诺(假设第一个和第二个数据不相互依赖)

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);
});

为了让事情更整洁,您可以更改 createPages 的 from:

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

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

还有 Promise 部分:

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

注意它变短了吗?

【讨论】:

  • 我认为您的第二个代码块可能有错误(虽然我看不出在哪里)。 FF 和 Chrome 说有一个未捕获的异常(in promise)
  • 这是显示未捕获异常的小提琴:jsfiddle.net/nw2zsahz(看看你的控制台,它应该会在几秒钟后出现)
  • 错误是因为 xhr.status === 0 导致的,编辑了答案以明确这一点
  • xhr.status 似乎为空 - 这是小提琴:jsfiddle.net/nw2zsahz/1(即 getData 函数未成功执行其 ajax 请求(
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-06-10
  • 1970-01-01
  • 1970-01-01
  • 2016-12-17
相关资源
最近更新 更多