【问题标题】:Variable Scope in Nested AJAX Calls嵌套 AJAX 调用中的变量范围
【发布时间】:2014-06-26 17:11:27
【问题描述】:

我有一组自定义用户数据,我想对其进行 ajax 调用,如果没有用户数据,则进行另一个 ajax 调用以检索默认数据集,然后执行一个函数解析数据。这是一个例子:

var oData = [],
    exampleUrl = 'example.php';
$.ajax({
    url:    exampleUrl + '?query=getUserData',
    contentType:    'application/json;odata=verbose',
    headers:        {
        'accept':   'application/json;odata=verbose'
    },
    success : function(data, request){
        // Request succeeded
        // Check the results
        if(data.length){
            // There are custom user results!
            // Parse the results
            oData = data;
        }
        else{
            // There were no custom user results...
            // Run another query to retrieve default values
            $.ajax({
                url:    examplUrl + '?query=getDefaultData',
                contentType:    'application/json;odata=verbose',
                headers:        {
                    'accept':   'application/json;odata=verbose'
                },
                success : function(data, request){
                    // Request succeeded
                    // Check the results
                    if(data.length){
                        // There was some default data!
                        // Parse the results
                        oData = data;
                    }
                    else{
                        // No data was found...
                        // Attempt to be helpful
                        console.log('No Default data was found!');
                    }
                },
                error : function(data, request){
                    // There was an error with the request
                    // Attempt to be helpful
                    console.log('Error retrieving data:');
                    console.log(data);
                    console.log(request);
                }
            });

        }
    },
    error : function(data, request){
        // There was an error with the request
        // Attempt to be helpful
        console.log('Error retrieving Custom User data:');
        console.log(data);
        console.log(request);
    },
    complete : function(){
        // Do something with the data
        index.displayData(oData);
    }
});

问题在于,如果运行第二个 ajax 调用,则 oData 在传递给index.displayData() 时根本不包含任何数据。我猜这与 ajax 调用的异步性质有关,但不应该在 'success' 中的所有内容运行之后'complete' 运行?

我也知道我可能不应该使用 ajax“末日金字塔”,而应该使用 Promise,但我已经尝试过它们并一直得到相同的结果。

感谢您的帮助!

【问题讨论】:

  • 您是否 100% 确定 data.length == 0?
  • 你是对的.. complete 绑定到 first ajax 调用。您在其中进行了单独的 ajax 调用,但这与第一个 ajax 调用是否实际完成无关。 IOW complete 并不意味着“整个事情以及其中的一切都已执行”。但是.. 有什么理由不能只进行一次 AJAX 调用并更改服务器端逻辑以在未找到用户数据的情况下仅返回默认数据?
  • @CrayonViolent 这是有道理的。不幸的是,我正在对 SharePoint 服务器进行 RESTful 调用,并且对服务器的响应方式没有太多控制。
  • 好的,那么我会将您的“默认”逻辑移动到第一个 ajax 调用的完整和 eval data 中,然后从那里进行第二次调用(如果需要)。

标签: javascript ajax asynchronous jquery-deferred


【解决方案1】:

正如 Violent Crayon 所指出的,您可以尝试自己调用“完成”,而不是依赖 JQuery 的隐式控制流:

function getData(exampleUrl, onComplete){
    $.ajax({
        success : function(data, request){
            if(data.length){
                onConplete(data);
            }else{
                $.ajax({
                    success : function(data, request){
                        if(data.length){
                            onComplete(data);
                        }else{
                            console.log('No Default data was found!');
                        }
                    },
                    error : function(data, request){
                        console.log('Error retrieving data:');
                    }
                });
            }
        },
        error : function(data, request){
            console.log('Error retrieving Custom User data:');
        }
    });
}

var oData = [];
getData('example.php', function(data){
    oData = data;
    index.displayData(oData);
}

顺便说一句,请注意如何让异步函数接收它们自己的返回和错误回调。这有助于减少末日金字塔问题,无需使用 Promise,也无需硬编码返回回调。

【讨论】:

  • 你让我大吃一惊@hugomg。我会试试这个。谢谢!
  • 做到了!非常感谢您的创新解决方案!
  • 谢谢,但我不是发明 continuation passing style 的人 :)
  • 无论如何,我感谢您的帮助。 :-)
  • 可以利用 Promise 方法提供更简洁的解决方案,避免嵌套并提供大大改进的错误消息。看我的回答。
【解决方案2】:

通过使用 Promise,您可以避免将回调传递给您的函数,并且通过定义实用函数可以避免代码重复。

//reusable utility function, which returns either a resolved or a rejected promise
function fetchData(queryString, cache) {
    return $.ajax({
        url: 'example.php',
        data: { query: queryString },
        type: 'JSON',//assumed
        cache: cache,
        contentType: 'application/json;odata=verbose',
        headers: { 'accept': 'application/json;odata=verbose' }
    }).then(function(data, textStatus, jqXHR) {
        if (data && data.length) {
            return data;
        } else {
            return $.Deferred().reject(jqXHR, 'no data returned').promise();//emulate a jQuery ajax failure
        }
    });
}

这允许将承诺方法用于控制结构,其中:

  • 简洁
  • 使用链接,而不是嵌套
  • 提供有意义的错误消息。
//control structure
fetchData('getUserData', false).then(null, function(jqXHR, textStatus) {
    console.log('Error retrieving Custom User data: ' + textStatus);
    return fetchData('getDefaultData', true);
}).then(index.displayData, function(jqXHR, textStatus) {
    console.log('Error retrieving default data: ' + textStatus);
});

注意事项:

  • .then(null, function(){...}) 中的null 允许成功响应直接传递到第二个.then(index.displayData, ...)
  • 默认数据被缓存,而用户数据不被缓存。这不是使事情正常运行所必需的,但下次需要默认数据时会更快。
  • 在承诺的世界中,这或类似的东西是要走的路。

【讨论】:

    猜你喜欢
    • 2023-03-28
    • 2013-05-03
    • 2015-07-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-09-25
    相关资源
    最近更新 更多