Gọi một chức năng sau khi chức năng trước hoàn thành


179

Tôi có mã JavaScript sau:

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

Làm thế nào tôi có thể đảm bảo rằng function2chỉ được gọi sau khi function1đã hoàn thành?


24
đang function1thực hiện một hoạt động không đồng bộ?
LiraNuna

8
Yads, nếu function1 chứa hình động, function2 sẽ được thực thi trong khi hoạt ảnh của function1 vẫn đang diễn ra. Làm thế nào bạn có thể làm cho hàm2 đợi cho đến khi mọi thứ bắt đầu trong hàm1 hoàn thành?
trusktr

Ai đó có thể giải thích cho tôi tại sao cần phải thực hiện bất cứ điều gì để đảm bảo hàm2 không được gọi cho đến khi hàm1 hoàn thành? Không có hoạt động không đồng bộ xảy ra ở đây vì vậy function2 sẽ không chạy cho đến khi function1 hoàn thành dù sao vì hoạt động này là đồng bộ.
Aaron

Câu trả lời:


206

Chỉ định một cuộc gọi lại ẩn danh và làm cho hàm1 chấp nhận nó:

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


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

11
+1 nhưng nếu anh ta sử dụng 1,5, anh ta chỉ có thể sử dụng mẫu Trì hoãn mới
philwinkle

Cảm ơn đã phản ứng nhanh chóng). Đúng, function1 chỉ đang thực hiện một tác vụ không đồng bộ đơn giản, vì vậy tôi tin rằng bạn chính xác ở chỗ tôi sẽ phải tạo một cuộc gọi lại - hoặc hãy xem doanh nghiệp bị trì hoãn này mà philwinkle đã đề cập. Cảm ơn một lần nữa, các bạn. Tôi sẽ ngủ một chút, và giải quyết chuyện này một lần nữa vào buổi sáng.
Rrryyyaaannn

13
Câu trả lời này không phải là giải pháp. Đây là bằng chứng nó không hoạt động: jsfiddle.net/trusktr/M2h6e
trusktr

12
@MikeRobinson Đây là vấn đề: Điều gì xảy ra nếu ...do stuffchứa những thứ không đồng bộ? Function2 vẫn sẽ không bắn khi dự kiến. Ví dụ trong nhận xét của tôi ở trên cho thấy rằng vì foo()hàm này có mã không đồng bộ trong đó, bar()nên không kích hoạt nội dung của nó sau khi nội dung của nó foo()được thực thi xong, thậm chí sử dụng $.when().then()để gọi chúng lần lượt. Câu trả lời này chỉ hợp lệ nếu (và chỉ khi) tất cả nội dung ...do stufftrong mã của bạn hoàn toàn đồng bộ.
trusktr

1
@trusktr Trong trường hợp đó, bạn cần tiếp tục chuyển cuộc gọi lại đầu tiên xuống cấp độ cuộc gọi không đồng bộ thứ n , sau đó thực hiện callBack()sau khi tất cả n cuộc gọi không đồng bộ được thực hiện. Vì OP đã không đề cập đến những gì anh ta đang làm trong chức năng, nên nó giả định nó là một cuộc gọi không đồng bộ một cấp.
GoodSp33d

96

Nếu bạn đang sử dụng jQuery 1.5, bạn có thể sử dụng mẫu Trì hoãn mới:

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

Chỉnh sửa: Cập nhật liên kết blog:

Rebecca Murphy đã có một bài viết tuyệt vời về vấn đề này tại đây: http://rmurphey.com/blog/2010/12/25/deferreds-asing-to-jquery/


2
Nhưng đó có phải là một ví dụ làm việc? Điều gì đang được hoãn lại? Bạn đang thực hiện chức năng1 và chức năng 2 ngay lập tức. (Tôi không phải là người bình luận ban đầu.)
user113716

10
Điều đó không làm việc cho tôi cả. function1 và function2 đều được thực thi cùng một lúc (có thể quan sát bằng mắt người). Đây là ví dụ nhỏ: jsfiddle.net/trusktr/M2h6e
trusktr

1
Rebecca Murphy viết lên trước khi Defferds được giới thiệu với jQuery và nó sử dụng các ví dụ dựa trên cách người viết nghĩ rằng nó sẽ được thực hiện. Vui lòng truy cập trang web của jQuery để xem cách thực sự sử dụng tính năng này: api.jquery.com/jQuery.Deferred
Spencer Williams

1
Ngay cả với jQuery 1.5 hoặc một cái gì đó hiện đại, bạn không muốn $.when(function1).then(function2);(không có dấu ngoặc đơn gọi hàm)?
Teepeemm

1
Đọc những bình luận này muộn vài năm. function1nên trả lại một lời hứa. Trong trường hợp đó, nó cần phải là hàm thực thi thực tế, không phải là tham chiếu. Khi resolveđược gọi vào lời hứa đó thì function2được thực thi. Điều này dễ dàng được giải thích bằng cách giả sử rằng function1có một cuộc gọi ajax. Cuộc donegọi lại sau đó sẽ giải quyết lời hứa. Tôi hy vọng điều đó rõ ràng.
philwinkle

46

Thử cái này :

function method1(){
   // some code

}

function method2(){
   // some code
}

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

37

Câu trả lời này sử dụng promises, một tính năng JavaScript của ECMAScript 6tiêu chuẩn. Nếu nền tảng mục tiêu của bạn không hỗ trợ promises, hãy hoàn thành nó với PromiseJs .

Hứa hẹn là một cách mới (và tốt hơn rất nhiều) để xử lý các hoạt động không đồng bộ trong 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
    });
} 

Điều này có vẻ như là một chi phí đáng kể cho ví dụ đơn giản này, nhưng đối với mã phức tạp hơn thì tốt hơn nhiều so với sử dụng các cuộc gọi lại. Bạn có thể dễ dàng xâu chuỗi nhiều cuộc gọi không đồng bộ bằng nhiều thencâu lệnh:

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

Bạn cũng có thể dễ dàng bao bọc các trình trì hoãn jQuery (được trả về từ $.ajaxcác cuộc gọi):

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

Như @charlietfl đã lưu ý, jqXHRđối tượng được trả về bằng cách $.ajax()thực hiện Promisegiao diện. Vì vậy, không thực sự cần thiết phải bọc nó trong một Promise, nó có thể được sử dụng trực tiếp:

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

Promise.resolvegói $.ajaxlà chống mẫu kể từ khi $.ajaxtrả lại lời hứa đáng chú ý
charlietfl

@charlietfl nhưng nó không phải là thực tế Promise, nó chỉ thực hiện giao diện. Tôi không chắc nó hoạt động như thế nào khi truyền thực tế Promisecho thenphương thức của nó , hoặc Promise.allsẽ làm gì nếu a jqXHRvà a Promiseđược truyền cho nó. Cho rằng tôi cần phải thử nghiệm thêm. Nhưng cảm ơn bạn đã gợi ý, tôi sẽ thêm nó vào câu trả lời!
Domysee

Thực tế trong phiên bản jQuery 3+ là Promise A + tuân thủ. Bất $.ajax.thenchấp không phải là mới
charlietfl

21

Hoặc bạn có thể kích hoạt một sự kiện tùy chỉnh khi một chức năng hoàn thành, sau đó liên kết nó với tài liệu:

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

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

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

Sử dụng phương pháp này, hàm 'b' chỉ có thể thực thi chức năng SAU 'a', vì trình kích hoạt chỉ tồn tại khi chức năng a thực hiện xong.


"Kể từ jQuery 1.7, phương thức .on () là phương thức ưa thích để đính kèm các trình xử lý sự kiện vào tài liệu." api.jquery.com/bind
CodeVirtuoso


3

Điều này phụ thuộc vào chức năng1 đang làm gì.

Nếu function1 đang thực hiện một số javascript đồng bộ đơn giản, như cập nhật giá trị div hoặc một cái gì đó, thì function2 sẽ kích hoạt sau khi function1 hoàn thành.

Nếu function1 thực hiện cuộc gọi không đồng bộ, chẳng hạn như cuộc gọi AJAX, bạn sẽ cần tạo phương thức "gọi lại" (hầu hết các API của ajax đều có tham số chức năng gọi lại). Sau đó gọi hàm2 trong cuộc gọi lại. ví dụ:

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

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

2

Nếu phương thức 1 phải được thực thi sau phương thức 2, 3, 4. Đoạn mã sau đây có thể là giải pháp cho việc này bằng cách sử dụng đối tượng Trì hoãn trong JavaScript.

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

Nếu function1 là một số chức năng đồng bộ hóa mà bạn muốn biến thành một async vì phải mất một thời gian để hoàn thành và bạn không có quyền kiểm soát nó để thêm một cuộc gọi lại:

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 ();

Đầu ra sẽ là:

Click handled

... và sau 2 giây:

This is function 1
This is function 2

Điều này hoạt động vì việc gọi window.setTimeout () sẽ thêm một tác vụ vào vòng lặp tác vụ runtine của JS, đây là điều mà một cuộc gọi async tạo ra và bởi vì nguyên tắc cơ bản là "chạy đến hoàn thành" của thời gian chạy JS đảm bảo rằng onClick () không bao giờ bị gián đoạn trước khi nó kết thúc.

Lưu ý rằng điều này thật buồn cười vì nó có thể là mã khó hiểu ...

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.