【问题标题】:Ajax promise error "Cannot read property 'then' of undefined" despite returning a promise尽管返回了承诺,但 Ajax 承诺错误“无法读取未定义的属性 'then'”
【发布时间】:2015-05-19 22:52:53
【问题描述】:

我正在尝试使用一个函数通过 3 个 ajax 调用来初始化数据,当这个函数完成后,我想对数据做一些事情。我必须等待完成 3 次 ajax 调用才能返回承诺。问题是,如果我等待完成 3 个 ajax 调用(使用 $.when (ajax1, ajax2, ajax3).done(...)),我会收到错误“无法读取未定义的属性 'then'”

这是我的代码

function myInit() {
    initData().then(function() {
        console.log("a");
    });
}

function initData() {
    var results = {
        categories: [],
        commandes: [],
        users: []
    };
    var ajaxCategories = $.ajax({
        url: url + '/categorie/getAll',
        success: function(data) {
            results.categories = data;
        }
    });

    var ajaxCommandes = $.ajax({
        url: url + '/statistique/getSimpleCommandes',
        success: function(data) {
            results.commandes = data;
        }
    });
    var ajaxUsers = $.ajax({
        url: url + '/statistique/getSimpleUsers',
        success: function(data) {
            results.users = data;
        }
    });
    $.when(ajaxUsers, ajaxCommandes, ajaxCategories)
        .done(function() {
            results.commandes.forEach(function(commande) {
                results.users.forEach(function(user) {
                    if (user.id == commande.user)
                        commande.user = user.nom;
                });
            });
            return $.Deferred().resolve();
        });
}

我在 $.when 中返回了一个承诺。如果我将此返回移到时间之外,它可以正常工作,但是我冒着退出函数并在未加载数据的情况下继续我的代码的风险。我的错在哪里?

【问题讨论】:

  • 我在initData 中看不到任何return - initData() 确实返回承诺。
  • 如果你想从回调中return,你需要使用.then 而不是.done
  • 在寻求帮助时,请花时间确保您的代码具有可读性和合理的格式。以上不是。 编辑不是,我已经为你修好了。
  • 谢谢,我真的想知道为什么当我尝试编辑它时它已经修复了

标签: jquery ajax promise


【解决方案1】:

done 的回调中返回一个承诺不会使initData 函数返回任何内容。

$.when 调用之外创建promise,以便您可以在回调中解决它并从initData 返回它:

var promise = $.Deferred();
$.when(ajaxUsers, ajaxCommandes, ajaxCategories)
    .done(function(){
        results.commandes.forEach(function(commande){
            results.users.forEach(function(user){
                if (user.id == commande.user)
                    commande.user = user.nom;
            });
        });
        promise.resolve();
    });
return promise.promise();

或者,返回 done 返回的承诺:

return $.when(ajaxUsers, ajaxCommandes, ajaxCategories)
    .done(function(){
        results.commandes.forEach(function(commande){
            results.users.forEach(function(user){
                if (user.id == commande.user)
                    commande.user = user.nom;
            });
        });
    });

使用done 方法添加的处理程序按照它们添加的顺序被调用,因此调用者添加的任何处理程序都将在您的处理程序之后运行。

如果您想在返回的 Promise 中传递来自 done 处理程序的数据,请在 resolve 调用中发送:

var promise = $.Deferred();
$.when(ajaxUsers, ajaxCommandes, ajaxCategories)
    .done(function(){
        results.commandes.forEach(function(commande){
            results.users.forEach(function(user){
                if (user.id == commande.user)
                    commande.user = user.nom;
            });
        });
        promise.resolve(results);
    });
return promise.promise();

【讨论】:

  • return promise; => return promise.promise(); 我们不想将完整的Deferred API 暴露给调用者。当然,$.when 已经返回了一个承诺。
  • 谢谢,它有效。我在 Bergi 提供的链接中读到,只有在我别无选择时才应该使用 Deferred。有没有更好的方法可以在不直接使用 $.Deferred 的情况下在这段代码中做我想做的事情?
  • @Percee:你也可以使用$.when方法创建的promise,然后done方法返回。
【解决方案2】:

initData() 不返回承诺,因为该函数中没有 return 语句。你需要回报$.when给你的承诺。

还要链接一个动作并从回调中返回一些东西,你需要使用.then 而不是.done

function initData() {
    var results = {
        categories: [],
        commandes: [],
        users: []
    };
    var ajaxCategories = $.ajax({
        url: url + '/categorie/getAll',
        success: function(data) {
            results.categories = data;
        }
    });

    var ajaxCommandes = $.ajax({
        url: url + '/statistique/getSimpleCommandes',
        success: function(data) {
            results.commandes = data;
        }
    });
    var ajaxUsers = $.ajax({
        url: url + '/statistique/getSimpleUsers',
        success: function(data) {
            results.users = data;
        }
    });
    return $.when(ajaxUsers, ajaxCommandes, ajaxCategories)
//  ^^^^^^
    .then(function() {
//   ^^^^
        results.commandes.forEach(function(commande) {
            results.users.forEach(function(user) {
                if (user.id == commande.user)
                    commande.user = user.nom;
            });
        });
        return results; // I guess that is what you wanted
    });
}

您甚至可以通过不使用全局 results 变量来进一步推进这一点,而是将其设置为回调的本地变量:

function initData() {
    return $.when(
        $.get(url + '/categorie/getAll'), 
        $.get(url + '/statistique/getSimpleCommandes'),
        $.get(url + '/statistique/getSimpleUsers')
    ).then(function(categoriesRes, commandesRes, userRes) {
        var results = {
            categories: categoriesRes[0],
            commandes: commandesRes[0],
            users: userRes[0]
        };
        results.commandes.forEach(function(commande) {
            results.users.forEach(function(user) {
                if (user.id == commande.user)
                    commande.user = user.nom;
            });
        });
        return results;
    });
}

【讨论】:

  • 如何使用那时返回的值?我不太明白它是返回一个承诺还是我的结果
  • 不,您不需要使用.then 代替.done。在您的示例中,.then.done 完全相同。
  • @Percee:它返回一个对结果的承诺——它们将作为参数提供给进一步的回调。例如initData.then(function(res) { console.log(res.commandes); })
  • @Guffa:因为我在我的回调中return 确实有很大的不同。你可以自己试试,或者看看stackoverflow.com/questions/5436327/…
  • @Bergi:很好,你是对的。我现在不必将它存储在 window.data 中:)
猜你喜欢
  • 1970-01-01
  • 2019-04-23
  • 2017-01-23
  • 2017-07-19
  • 2019-03-10
  • 1970-01-01
  • 2023-03-31
  • 2018-08-17
  • 2020-04-27
相关资源
最近更新 更多