首页 > 解决方案 > jquery延迟返回太早

问题描述

我有以下代码:

$.when(multipleQueries(stateQueries, rowData))
    .then(function (data) {
        //do stuff with data - removed for brevity

多重查询功能如下:

function multipleQueries(queriesToExecute, rowData) {
    var allQueriesMapped = $.Deferred();

    // If a single query has been provided, convert it into an array
    if (Array.isArray(queriesToExecute) === false) {
        queriesToExecute = [].concat(queriesToExecute);
    }

    // Create a function for each region to run the query.
    $.when.apply($, $.map(queriesToExecute, function (query) {

        // Execute the query in the region
        return $.when(executeQuery(query.InstanceInfo, query.Query)).then(function (data) {
            var isDataMapped = $.Deferred();
            var mappedData = [];
            // Perform some data transformation
            $.when.apply($, $.map(data.results, function (value) {
                var properties = value.succinctProperties;
                 //code removed for brevity
                return $.when(mapData(properties)).then(function (mappedRow) {
                    if (mappedRow) {
                        mappedData.push(mappedRow);
                    }
                });
            })).then(function () {
                isDataMapped.resolve({
                    results: mappedData,
                    numItems: mappedData.length
                });
            });
            return isDataMapped.promise();
        }).then(function (data) {
            debugger;
            allQueriesMapped.resolve(data);
        });
    }));

    return allQueriesMapped.promise();
}

我遇到的问题是,我传入了 5 个查询来执行 multipleQueries 函数,但它在运行第一个查询后到达调试器行 - 然后解析 allQueriesMapped deferred,然后返回到 do 数据它是从哪里调用的,但是因为我没有来自我传入的 5 个 queires 的所有数据,所以我没有看到预期的行为 - 我如何设置这些承诺有什么遗漏吗?

注意 - 我尝试将调试器之前的 .then 更改为 .done 但得到相同的行为,并且还尝试将调用代码更改为 .done ,如下所示,但也得到相同的结果。

$.when(multipleQueries(stateQueries, rowData))
    .done(function (data) {
        //do stuff with data - removed for brevity

** 更新 - 执行查询功能如下

function executeQuery(instanceInfo, query) {
    return $.ajax({
        url: instanceInfo.Url,
        type: 'GET',
        data: {
            q: query,
            succinct: true
        },
        processData: true
    });
}

标签: javascriptjquerypromisejquery-deferred

解决方案


正如 freen-m 和 charlietfl 指出的那样,这then是在错误的地方(见***评论):

function multipleQueries(queriesToExecute, rowData) {
    var allQueriesMapped = $.Deferred();

    // If a single query has been provided, convert it into an array
    if (Array.isArray(queriesToExecute) === false) {
        queriesToExecute = [].concat(queriesToExecute);
    }

    // Create a function for each region to run the query.
    $.when.apply($, $.map(queriesToExecute, function(query) {

        // Execute the query in the region
        return $.when(executeQuery(query.InstanceInfo, query.Query)).then(function(data) {
            var isDataMapped = $.Deferred();
            var mappedData = [];
            // Perform some data transformation
            $.when.apply($, $.map(data.results, function(value) {
                var properties = value.succinctProperties;
                //code removed for brevity
                return $.when(mapData(properties)).then(function(mappedRow) {
                    if (mappedRow) {
                        mappedData.push(mappedRow);
                    }
                });
            })).then(function() {
                isDataMapped.resolve({
                    results: mappedData,
                    numItems: mappedData.length
                });
            });
            return isDataMapped.promise();
        }).then(function(data) {                    // ***
            debugger;                               // ***
            allQueriesMapped.resolve(data);         // ***
        });
    }));

    return allQueriesMapped.promise();
}

它在里面map,当它应该在外面时:

function multipleQueries(queriesToExecute, rowData) {
    var allQueriesMapped = $.Deferred();

    // If a single query has been provided, convert it into an array
    if (Array.isArray(queriesToExecute) === false) {
        queriesToExecute = [].concat(queriesToExecute);
    }

    // Create a function for each region to run the query.
    $.when.apply($, $.map(queriesToExecute, function(query) {

        // Execute the query in the region
        return $.when(executeQuery(query.InstanceInfo, query.Query)).then(function(data) {
            var isDataMapped = $.Deferred();
            var mappedData = [];
            // Perform some data transformation
            $.when.apply($, $.map(data.results, function(value) {
                var properties = value.succinctProperties;
                //code removed for brevity
                return $.when(mapData(properties)).then(function(mappedRow) {
                    if (mappedRow) {
                        mappedData.push(mappedRow);
                    }
                });
            })).then(function() {
                isDataMapped.resolve({
                    results: mappedData,
                    numItems: mappedData.length
                });
            });
            return isDataMapped.promise();
        });
    })).then(function(data) {
        debugger;
        allQueriesMapped.resolve(data);
    });

    return allQueriesMapped.promise();
}

但是那里有很多不必要的使用$.whenand new $.Deferred(见*** 1评论),你可以更简单地将你的参数包装在一个数组中(见*** 2评论:

function multipleQueries(queriesToExecute, rowData) {
    // If a single query has been provided, convert it into an array
    if (Array.isArray(queriesToExecute) === false) {
        queriesToExecute = [queriesToExecute]; // *** 2
    }

    // Create a function for each region to run the query.
    return $.when.apply($, $.map(queriesToExecute, function(query) { // *** 1

        // Execute the query in the region
        return executeQuery(query.InstanceInfo, query.Query).then(function(data) { // *** 1
            var mappedData = [];
            // Perform some data transformation
            return $.when.apply($, $.map(data.results, function(value) {
                var properties = value.succinctProperties;
                //code removed for brevity
                return mapData(properties).then(function(mappedRow) { // *** 1
                    if (mappedRow) {
                        mappedData.push(mappedRow);
                    }
                });
            })).then(function() {
                return {
                    results: mappedData,
                    numItems: mappedData.length
                };
            });
        });
    }));
}

new当你已经有了一个承诺时,就再也不需要通过;创建一个新的承诺了。只需使用返回的then. 此外,当您已经有一个承诺时,就永远不需要使用$.when(thePromise).

您可能还受益于切换到内置的 Promise 语义而不是Deferred早期的 jQuery:

function multipleQueries(queriesToExecute, rowData) {
    // If a single query has been provided, convert it into an array
    if (Array.isArray(queriesToExecute) === false) {
        queriesToExecute = [queriesToExecute];
    }

    // Create a function for each region to run the query.
    return Promise.all(queriesToExecute.map(function(query) {
        // Execute the query in the region
        return executeQuery(query.InstanceInfo, query.Query).then(function(data) {
            return Promise.all(data.results.map(function(value) {
                var properties = value.succinctProperties;
                //code removed for brevity
                return mapData(properties);
            }).then(function(mappedData) {
                mappedData = mappedData.filter(Boolean); // Remove the falsy ones from `mapData`
                return {
                    results: mappedData,
                    numItems: mappedData.length
                };
            });
        });
    }));
}

Promise.all对于处理一系列承诺非常有用。但请确保您使用的是最新的 jQuery,早期版本的 Deferred 无法与真正的 Promise 正确互操作。我不知道(也无法立即找到)该问题何时修复。


推荐阅读