【问题标题】:jquery deferred with async callsjquery 被异步调用延迟
【发布时间】:2017-07-18 12:55:58
【问题描述】:

请找到下面的代码sn-p:

HTML:

<div>
<span> First Name : <input type="text" id="firstName" name="First Name"/></span>
</div>
<br/>
<div>
<span>Student Id: <input type="text" id="studentId" name="studentId"/></span>
<span>Teacher Id: <input type="text" id="teacherId" name="teacherId"/></span>
</div>
<br/>
<div>
<span>Student Name : <input type="text" id="stdLastName" name="stdLastName"/></span>
<span>Student Age :<input type="text" id="stdAge" name="stdAge"/></span>
</div>
<br/>
<div>
<span>Teacher Name : <input type="text" id="tchrLastName" name="tchrLastName"/></span>
<span>Teacher Age : <input type="text" id="tchrAge" name="tchrAge"/></span>
</div>
<br/>
<input type="button" value="Submit" id="submit"/>

Javascript:

    $('#firstName').focus();

var d1= new $.Deferred();

$('#firstName').blur(populatePage());

//called on blur of first name 
function populatePage() {
    $.when(populateStdDetails(),populateTchrDetails()).done(function(resp1, resp2){
          $('#stdLastName').val(resp1[0].stdName);
        $('#stdAge').val(resp1[0].age);
        $('#tchrLastName').val(resp2[0].stdName);
        $('#tchrAge').val(resp2[0].age);
            console.log('All details populated....');
        d1.resolve();

  });
  return d1;
}  

//first ajax call
function populateStdDetails() {
    if($('#firstName').val() != '' && $('#studentId').val() !='') {
        return $.ajax({
        url : '/someURL?studentId='+studentId+'&firstName='+firstName,
      type :'GET',
      contentType:'json'
    });
    }
}

//second ajax call
function populateTchrDetails() {
    if($('#firstName').val() != '' && $('#teacherId').val() !='') {
        return $.ajax({
        url : '/someURL?teacherId='+teacherId+'&firstName='+firstName,
      type :'GET',
      contentType:'json'
    });
    }
}

$('#submit').click(function(e){
    //wait for the ajax calls to be completed
  $.when(populatePage()).done(function(e){
    console.log('All done !!!!');
    //Move to next page;
  });
});

First Name 文本字段附加了一个 onblur 事件,在通常情况下可以正常工作,但是当焦点位于“First Name”并单击“Submit”时,将调用提交函数而不是等待 onblur 事件发生完成。

【问题讨论】:

  • 请注意,在实际代码中,jQuery 中的 ajax 请求已经返回了自己的 Promise,因此您不需要将它们包装在另一个 Promise 中。如果您向我们展示您的真实代码,我们实际上可以分享如何在真实代码中最好地使用 Promise。假设代码的理论问题很少像真正的代码那样有价值,贡献者可以向您展示您可以做得更好的所有事情。
  • @jfriend00:请找到我描述实际需求的小提琴:jsfiddle.net/diptesh2007/ebz6ey1m
  • 这段代码有很多问题。首先,它充满了deferred anti-pattern。如果您按照此处的发布指南将该代码放入您的问题中,我们可以向您展示很多更好的方法来做您正在做的事情。正如我所怀疑的,您根本不需要创建新的 Promise,因为您已经拥有来自 $.ajax() 调用的 Promise,可以直接使用。
  • 是的,我同意你的观点,即不需要从 ajax 解析 Promise,因为它可以直接使用,但实际上我们已经在 ajax 上构建了一个包装器,它只允许调用者实现回调。无论如何,我已经用更实际的代码 sn-p 更新了原始问题。

标签: ajax jquery-deferred


【解决方案1】:

您在超时函数中将 deferred.resolve 放置在错误的位置。这样做:

function doSomething(deffered) {
    $('#log').append('doSomething');
    deferred.resolve();
    return deferred;
};

function ajaxRequests1(deferred) {
    setTimeout(function(){
        $('#log').append('......ajaxRequests1');
        deferred.resolve(); 
    }, 1000);
    return deferred;
};

function ajaxRequests2(deferred) {
   setTimeout(function(){
        $('#log').append('.....ajaxRequests2');
        deferred.resolve();
   }, 5000); 
   return deferred;
};

var func1 = function () {
        var promise = new $.Deferred();
        ajaxRequests1(promise);
        return promise;
    }

var func2 = function () {
        var promise = new $.Deferred();
        ajaxRequests2(promise);
        return promise;
}

var stepFinal = function() {
       var promise = new $.Deferred();
       doSomething(promise);
       return promise;
}

$.when(func1().promise(), func2().promise())
    .done(function () {
            stepFinal().done();
    });

【讨论】:

    【解决方案2】:

    好的,如果您希望在焦点离开#firstname 时调用populatePage(),并且如果用户还单击了提交按钮并且您希望提交操作等待该模糊操作完成,您可以这样做:

    $('#firstName').blur(function(e) {
        // call populatePage and set the resulting promise as a data item so
        // the submit handler can get access to it
        var self = $(this);
        var p = populatePage();
        self.data("blurPromise", p);
    
        // when this promise is done, clear the blurPromise
        p.always(function() {
            self.removeData("blurPromise");
        });
    });
    
    //called on blur of first name 
    function populatePage() {
        return $.when(populateStdDetails(),populateTchrDetails()).done(function(resp1, resp2){
            $('#stdLastName').val(resp1[0].stdName);
            $('#stdAge').val(resp1[0].age);
            $('#tchrLastName').val(resp2[0].stdName);
            $('#tchrAge').val(resp2[0].age);
            console.log('All details populated....');
        });
    }  
    
    //first ajax call
    function populateStdDetails() {
        if($('#firstName').val() != '' && $('#studentId').val() !='') {
            return $.ajax({
                url : '/someURL?studentId='+studentId+'&firstName='+firstName,
                type :'GET',
                contentType:'json'
            });
        } else {
            // just return already resolved promise
            return $.when();
        }
    }
    
    //second ajax call
    function populateTchrDetails() {
        if($('#firstName').val() != '' && $('#teacherId').val() !='') {
            return $.ajax({
                url : '/someURL?teacherId='+teacherId+'&firstName='+firstName,
                type :'GET',
                contentType:'json'
            });
        } else {
            return $.when();
        }
    }
    
    $('#submit').click(function(e){
        // get blur promise or dummy resolved promise
        var p = $("#firstName").data("blurPromise") || $.when();
        p.then(function() {
            // do your submit logic here
            // The onBlur handler is done now
        });
    });
    

    我在您的承诺处理代码中更新的内容:

    1. 直接使用 $.ajax() 承诺,而无需将它们包装在另一个承诺中。
    2. 直接使用 $.when() 承诺,而无需将它们包装在另一个承诺中。
    3. 当使用if 语句来决定是否执行异步操作时,通常最好在else 分支中也返回一个promise,这样您的函数始终返回一个promise。如果在 else 子句中没有其他事情可做,那么在 jQuery 中返回已解决的承诺的捷径就是 return $.when();
    4. 请注意,.done() 是 jQuery 特定的,而不是标准的承诺行为。如果您已经在使用 jQuery 3.x 或更高版本,那么您可能应该切换到 .then(),然后您的 Promise 就会像 Promise 标准一样运行。

    【讨论】:

    • 感谢您的回复。由于第一个文本字段附加了一个 onblur 事件,请考虑当焦点位于字段“名字”并单击“提交”时会发生什么。 onblur 事件被调用,并且“提交”函数也将被调用。我希望在提交功能执行之前完成 onblur 功能。你认为 e.preventDefault() 可以防止模糊事件吗?
    • @diptesh2007 - 我试图了解这里的所有用例。如果焦点在名字上并且用户单击提交按钮,您是否只想调用一次populatePage(),然后在完成后提交表单?如果用户在其他领域,您是否还想在提交之前调用一次populatePage(),然后在完成后提交表单?那么,您是否只是想在失去焦点或提交时调用populatePage()?另外,是否有实际的&lt;form&gt;?您的 HTML 不显示这些标签。
    • 我希望仅在当前正在发生的“名字”字段的 onblur 期间调用 populatePage()。在提交期间,我只想调用提交函数,而不管哪个字段具有焦点,但是如果“名字”具有焦点并单击提交,则当前填充 Page() 将被调用。在我们的例子中,表单是通过 ajax 提交的,而不是通过默认的表单操作。
    • @diptesh2007 - 好的,我完全修改了我的答案来做我认为你想要的。
    • @diptesh2007 - 这能回答你的问题吗?如果是这样,您可以通过单击答案左侧的绿色复选标记向社区表明这一点,这也将为您赢得一些声誉积分。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-22
    • 2016-09-03
    • 1970-01-01
    • 2016-02-02
    • 1970-01-01
    相关资源
    最近更新 更多