上一个功能完成后调用一个功能


178

我有以下JavaScript代码:

$('a.button').click(function(){
    if (condition == 'true'){
        function1(someVariable);
        function2(someOtherVariable);
    }
    else {
        doThis(someVariable);
    }
});

我如何确保function2仅在function1完成后调用?


24
正在function1执行异步操作?
LiraNuna

8
Yads,如果function1包含动画,则在function1的动画仍在发生时将执行function2。您将如何让function2等到在function1中启动的所有操作完全完成?
trusktr 2011年

有人可以向我解释为什么需要做些什么来确保在function1完成之前不调用function2吗?这里没有发生异步操作,因此function2将一直运行,直到function1完成为止,因为此操作是同步的。
亚伦

Answers:


204

指定一个匿名回调,并使function1接受它:

$('a.button').click(function(){
    if (condition == 'true'){
        function1(someVariable, function() {
          function2(someOtherVariable);
        });
    }
    else {
        doThis(someVariable);
    }
});


function function1(param, callback) {
  ...do stuff
  callback();
} 

11
+1,但如果他使用1.5,则可以使用新的Deferreds模式
philwinkle

感谢您的快速回复)。是的,function1只是执行一个简单的异步任务,所以我认为您是对的,因为我必须创建一个回调-或看看philwinkle提到的这种递延业务。再次感谢你们。我要去睡一会儿,早上再解决一次。
Rrryyyaaannn 2011年

13
这个答案不是解决方案。这证明它不起作用:jsfiddle.net/trusktr/M2h6e
trusktr 2011年

12
@MikeRobinson这是问题所在:如果...do stuff包含异步的内容怎么办?如预期的那样,Function2仍不会触发。我上面的注释中的示例显示,由于该foo()函数中包含异步代码,因此即使bar()foo()内容执行完毕后也不会触发它的内容,即使使用$.when().then()一个接一个地调用它们也是如此。仅当(并且仅)...do stuff代码中的所有内容严格同步时,此答案才有效。
trusktr

1
@trusktr在这种情况下,您需要将第一个回调向下传递到第n个异步调用级别,然后callBack()在完成所有n个异步调用之后触发。由于OP并未提及他在该函数中所做的工作,因此将其视为一级异步调用。
GoodSp33d

96

如果您使用的是jQuery 1.5,则可以使用新的Deferreds模式:

$('a.button').click(function(){
    if(condition == 'true'){
        $.when(function1()).then(function2());
    }
    else {
        doThis(someVariable);
    }
});

编辑:更新了博客链接:

丽贝卡·墨菲(Rebecca Murphy)在这里对此进行了出色的撰写:http : //rmurphey.com/blog/2010/12/25/deferreds-coming-to-jquery/


2
但这是一个可行的例子吗?推迟了什么?您正在立即执行function1和function2。(我不是原始评论员。)
user113716 2011年

10
这对我根本不起作用。function1和function2都在完全相同的时间执行(人眼可以观察到)。这是一个小例子:jsfiddle.net/trusktr/M2h6e
trusktr 2011年

1
丽贝卡·墨菲(Rebecca Murphy)的撰写是在将Defferds引入jQuery之前,它使用了一些基于作者认为将要实现的示例。请访问jQuery的网站以了解如何实际使用此功能:api.jquery.com/jQuery.Deferred
Spencer Williams

1
即使使用jQuery 1.5或现代的东西,您是否也不需要$.when(function1).then(function2);(没有调用函数的括号)?
Teepeemm 2014年

1
几年后阅读这些评论。 function1应该兑现承诺。在这种情况下,它必须是实际执行的功能,而不是引用。当resolve在那个诺言上被调用时,然后function2执行。通过假定function1具有ajax调用,可以很容易地解释这一点。然后,done回调将解决承诺。我希望这很清楚。
philwinkle

46

试试这个 :

function method1(){
   // some code

}

function method2(){
   // some code
}

$.ajax({
   url:method1(),
   success:function(){
   method2();
}
})

37

该答案使用promises,是ECMAScript 6标准的JavaScript功能。如果目标平台不支持promises,请用PromiseJs填充它。

承诺是一种处理JavaScript中异步操作的新方法(更好的方法):

$('a.button').click(function(){
    if (condition == 'true'){
        function1(someVariable).then(function() {
            //this function is executed after function1
            function2(someOtherVariable);
        });
    }
    else {
        doThis(someVariable);
    }
});


function function1(param, callback) {
    return new Promise(function (fulfill, reject){
        //do stuff
        fulfill(result); //if the action succeeded
        reject(error); //if the action did not succeed
    });
} 

对于这个简单的示例,这似乎是一笔巨大的开销,但是对于更复杂的代码,这比使用回调要好得多。您可以使用多个then语句轻松地链接多个异步调用:

function1(someVariable).then(function() {
    function2(someOtherVariable);
}).then(function() {
    function3();
});

您还可以轻松包装jQuery延迟(从$.ajax调用返回):

Promise.resolve($.ajax(...params...)).then(function(result) {
    //whatever you want to do after the request
});

如@charlietfl所指出的,由jqXHR返回的对象$.ajax()实现Promise接口。因此,实际上没有必要将其包装在中Promise,可以直接使用:

$.ajax(...params...).then(function(result) {
    //whatever you want to do after the request
});

Promise.resolve包装$.ajax是因为反模式$.ajax的回报thennable承诺
charlietfl

@charlietfl但这不是实际的Promise,它只是实现了接口。我不确定将实数传递Promise给它的then方法时它的行为,或者Promise.all将a jqXHR和a Promise传递给它时a 会做什么。为此,我需要做进一步的测试。但是,谢谢您的提示,我将其添加到答案中!
米湖

实际上,在jQuery 3+中,它符合Promises A +。不管$.ajax.then是什么都不新鲜
charlietfl '18

21

或者,您可以在一个函数完成时触发自定义事件,然后将其绑定到文档:

function a() {
    // first function code here
    $(document).trigger('function_a_complete');
}

function b() {
    // second function code here
}

$(document).bind('function_a_complete', b);

使用此方法,函数“ b”只能在函数“ a”之后执行,因为仅当函数a完成执行时才存在触发器。


“从jQuery 1.7开始,.on()方法是将事件处理程序附加到文档的首选方法。” api.jquery.com/bind
CodeVirtuoso 2013年

3

这取决于function1在做什么。

如果function1在执行一些简单的同步javascript,例如更新div值或其他操作,则function2将在function1完成后触发。

如果function1正在进行异步调用(例如AJAX调用),则需要创建一个“回调”方法(大多数ajax API都有一个回调函数参数)。然后在回调中调用function2。例如:

function1()
{
  new AjaxCall(ajaxOptions, MyCallback);
}

function MyCallback(result)
{
  function2(result);
}

3

你可以这样

$.when(funtion1()).then(function(){
    funtion2();
})

2

如果必须在方法2、3、4之后执行方法1,则以下代码段可以是使用JavaScript中的Deferred对象的解决方案。

function method1(){
  var dfd = new $.Deferred();
     setTimeout(function(){
     console.log("Inside Method - 1"); 
     method2(dfd);	 
    }, 5000);
  return dfd.promise();
}

function method2(dfd){
  setTimeout(function(){
   console.log("Inside Method - 2"); 
   method3(dfd);	
  }, 3000);
}

function method3(dfd){
  setTimeout(function(){
   console.log("Inside Method - 3"); 	
   dfd.resolve();
  }, 3000);
}

function method4(){   
   console.log("Inside Method - 4"); 	
}

var call = method1();

$.when(call).then(function(cb){
  method4();
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>


0

如果function1是某个同步功能,您希望将其转换为一个异步功能,因为它需要一些时间才能完成,并且您无法控制它来添加回调:

function function1 (someVariable) {
    var date = Date.now ();
    while (Date.now () - date < 2000);      // function1 takes some time to complete
    console.log (someVariable);
}
function function2 (someVariable) {
    console.log (someVariable);
}
function onClick () {
    window.setTimeout (() => { function1 ("This is function1"); }, 0);
    window.setTimeout (() => { function2 ("This is function2"); }, 0);
    console.log ("Click handled");  // To show that the function will return before both functions are executed
}
onClick ();

输出将是:

Click handled

...并在2秒后:

This is function 1
This is function 2

之所以可行,是因为调用window.setTimeout()会将任务添加到JS runtine任务循环中,这是异步调用的原因,并且因为JS运行时的“运行完成”的基本原理确保onClick()在结束之前永远不会中断。

请注意,这很有趣,可能使代码难以理解...

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.