Làm thế nào tôi nên gọi 3 hàm để thực hiện lần lượt từng hàm?


151

Nếu tôi cần gọi chức năng này lần lượt

$('#art1').animate({'width':'1000px'},1000);        
$('#art2').animate({'width':'1000px'},1000);        
$('#art3').animate({'width':'1000px'},1000);        

Tôi biết trong jQuery tôi có thể làm một cái gì đó như:

$('#art1').animate({'width':'1000px'},1000,'linear',function(){
    $('#art2').animate({'width':'1000px'},1000,'linear',function(){
        $('#art3').animate({'width':'1000px'},1000);        
    });        
});        

Nhưng, giả sử rằng tôi không sử dụng jQuery và tôi muốn gọi:

some_3secs_function(some_value);        
some_5secs_function(some_value);        
some_8secs_function(some_value);        

Làm thế nào tôi nên gọi các chức năng này để thực hiện some_3secs_functionvà SAU cuộc gọi đó kết thúc, sau đó thực hiện some_5secs_functionvà SAU cuộc gọi đó kết thúc, sau đó gọi some_8secs_function?

CẬP NHẬT:

Điều này vẫn không hoạt động:

(function(callback){
    $('#art1').animate({'width':'1000px'},1000);
    callback();
})((function(callback2){
    $('#art2').animate({'width':'1000px'},1000);
    callback2();
})(function(){
    $('#art3').animate({'width':'1000px'},1000);
}));

Ba hình ảnh động bắt đầu cùng một lúc

Lỗi của tôi ở đâu?


bạn có nghĩa là các chức năng được gọi trong chính xác 3 5 & 8 giây hoặc chỉ lần lượt từng cái khác?
Trass Vasston

Tôi nghĩ đơn giản là bạn không chắc chắn về việc thực thi chức năng đồng bộ và không đồng bộ. Tôi đã cập nhật câu trả lời của tôi dưới đây. Hy vọng nó giúp.
Wayne

Hãy thử điều này .. github.com/dineshkani24/queuecall
Dineshkani

Câu trả lời:


243

Trong Javascript, có đồng bộkhông đồng bộ chức năng.

Chức năng đồng bộ

Hầu hết các chức năng trong Javascript là đồng bộ. Nếu bạn đã gọi một số chức năng đồng bộ liên tiếp

doSomething();
doSomethingElse();
doSomethingUsefulThisTime();

họ sẽ thực hiện theo thứ tự. doSomethingElsesẽ không bắt đầu cho đến khi doSomethinghoàn thành. doSomethingUsefulThisTime, lần lượt, sẽ không bắt đầu cho đến khi doSomethingElsehoàn thành.

Hàm không đồng bộ

Tuy nhiên, chức năng không đồng bộ sẽ không chờ nhau. Chúng ta hãy xem xét cùng một mẫu mã mà chúng ta đã có ở trên, lần này giả sử rằng các hàm không đồng bộ

doSomething();
doSomethingElse();
doSomethingUsefulThisTime();

Các chức năng sẽ được khởi tạo theo thứ tự, nhưng tất cả chúng sẽ thực hiện gần như cùng một lúc. Bạn không thể dự đoán một cách nhất quán cái nào sẽ hoàn thành trước: cái nào xảy ra trong khoảng thời gian ngắn nhất để thực hiện sẽ hoàn thành trước.

Nhưng đôi khi, bạn muốn các hàm không đồng bộ thực hiện theo thứ tự và đôi khi bạn muốn các hàm được đồng bộ hóa để thực thi không đồng bộ. May mắn thay, điều này là có thể với các cuộc gọi lại và thời gian chờ, tương ứng.

Gọi lại

Giả sử rằng chúng ta có ba chức năng không đồng bộ mà chúng ta muốn thực hiện theo thứ tự, some_3secs_function, some_5secs_function, và some_8secs_function.

Vì các hàm có thể được truyền dưới dạng đối số trong Javascript, bạn có thể truyền một hàm dưới dạng gọi lại để thực thi sau khi hàm hoàn thành.

Nếu chúng ta tạo các hàm như thế này

function some_3secs_function(value, callback){
  //do stuff
  callback();
}

sau đó bạn có thể gọi sau đó theo thứ tự, như thế này:

some_3secs_function(some_value, function() {
  some_5secs_function(other_value, function() {
    some_8secs_function(third_value, function() {
      //All three functions have completed, in order.
    });
  });
});

Hết giờ

Trong Javascript, bạn có thể yêu cầu một chức năng thực thi sau một khoảng thời gian chờ nhất định (tính bằng mili giây). Điều này có thể, trong thực tế, làm cho các chức năng đồng bộ hoạt động không đồng bộ.

Nếu chúng ta có ba hàm đồng bộ, chúng ta có thể thực thi chúng không đồng bộ bằng cách sử dụng setTimeouthàm.

setTimeout(doSomething, 10);
setTimeout(doSomethingElse, 10);
setTimeout(doSomethingUsefulThisTime, 10);

Tuy nhiên, điều này hơi xấu và vi phạm nguyên tắc DRY [wikipedia] . Chúng ta có thể làm sạch điều này một chút bằng cách tạo một hàm chấp nhận một mảng các hàm và thời gian chờ.

function executeAsynchronously(functions, timeout) {
  for(var i = 0; i < functions.length; i++) {
    setTimeout(functions[i], timeout);
  }
}

Điều này có thể được gọi như vậy:

executeAsynchronously(
    [doSomething, doSomethingElse, doSomethingUsefulThisTime], 10);

Tóm lại, nếu bạn có các hàm không đồng bộ mà bạn muốn thực thi đồng bộ hóa, hãy sử dụng các hàm gọi lại và nếu bạn có các hàm đồng bộ mà bạn muốn thực thi không đồng bộ, hãy sử dụng thời gian chờ.


7
Điều này sẽ không trì hoãn các chức năng trong 3,5 và 8 giây, như ví dụ được đề xuất, chúng sẽ chỉ chạy từng cái một.
Trass Vasston

1
@Peter - Đợi đã, vậy là tôi bối rối. Nếu đây là những cuộc gọi đồng bộ đơn giản xảy ra trong vài giây để hoàn thành, vậy tại sao chúng ta cần bất kỳ cuộc gọi nào trong số này?
Wayne

9
@Peter - +1 cho phương thức đẹp nhất, phức tạp nhất mà tôi từng thấy khi gọi ba hàm đồng bộ theo trình tự.
Wayne

4
Cảm ơn bạn đã giải thích một cách thành thạo sự khác biệt giữa các chức năng js async và sync. Điều này giải thích rất nhiều.
jnelson

2
Điều này KHÔNG chính xác vì các lý do sau: (1) 3 thời gian chờ sẽ giải quyết sau 10 giây, do đó cả 3 dòng kích hoạt cùng một lúc. (2) phương pháp này yêu cầu bạn phải biết thời lượng trước và các chức năng "lên lịch" sẽ xảy ra trong tương lai, thay vì chờ đợi các chức năng không đồng bộ trước đó trong chuỗi để giải quyết và đó là trình kích hoạt. --- thay vào đó bạn muốn sử dụng một trong những câu trả lời sau bằng cách sử dụng các cuộc gọi lại, lời hứa hoặc thư viện async.
zeroasterisk

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ãy xem câu trả lời của tôi ở đây Đợi cho đến khi một Chức năng có hoạt ảnh kết thúc cho đến khi chạy một Chức năng khác nếu bạn muốn sử dụng jQueryhoạt hình.

Đây là những gì mã của bạn sẽ trông như thế nào với ES6 PromisesjQuery animations.

Promise.resolve($('#art1').animate({ 'width': '1000px' }, 1000).promise()).then(function(){
    return Promise.resolve($('#art2').animate({ 'width': '1000px' }, 1000).promise());
}).then(function(){
    return Promise.resolve($('#art3').animate({ 'width': '1000px' }, 1000).promise());
});

Phương pháp bình thường cũng có thể được bọc trong Promises.

new Promise(function(fulfill, reject){
    //do something for 5 seconds
    fulfill(result);
}).then(function(result){
    return new Promise(function(fulfill, reject){
        //do something for 5 seconds
        fulfill(result);
    });
}).then(function(result){
    return new Promise(function(fulfill, reject){
        //do something for 8 seconds
        fulfill(result);
    });
}).then(function(result){
    //do something with the result
});

Các thenphương pháp được thực hiện ngay sau khi Promisehoàn thành. Thông thường, kết quả trả về giá trị functionđược chuyển đến thenđược chuyển sang giá trị tiếp theo.

Nhưng nếu a Promiseđược trả về, thenhàm tiếp theo sẽ đợi cho đến khi Promisethực hiện xong và nhận được kết quả của nó (giá trị được chuyển đến fulfill).


Tôi biết điều này là hữu ích, nhưng tìm thấy mã được đưa ra khó hiểu mà không tìm kiếm một ví dụ thực tế để cung cấp cho nó một số bối cảnh. Tôi tìm thấy video này trên YouTube: youtube.com/watch?v=y5mltEaQxa0 - và viết lên nguồn từ video ở đây drive.google.com/file/d/1NrsAYs1oaxXw0kv9hz7a6LjtOEb6x7z-/... Có một số sắc thái hơn như bắt mất tích trong ví dụ này mà công phu trên. (sử dụng một id khác trong dòng getPostById () hoặc thử thay đổi tên của tác giả để nó không khớp với bài đăng, v.v.)
JGFMK

20

Có vẻ như bạn không hoàn toàn đánh giá cao sự khác biệt giữa thực thi chức năng đồng bộkhông đồng bộ .

Mã bạn cung cấp trong bản cập nhật của bạn ngay lập tức thực thi từng chức năng gọi lại của bạn, từ đó ngay lập tức bắt đầu một hình ảnh động. Những hình ảnh động, tuy nhiên, thực asyncronously . Nó hoạt động như thế này:

  1. Thực hiện một bước trong hoạt hình
  2. Gọi setTimeoutvới chức năng chứa bước hoạt hình tiếp theo và độ trễ
  3. Thời gian trôi qua
  4. Cuộc gọi lại được setTimeoutthực hiện để thực thi
  5. Quay trở lại bước 1

Điều này tiếp tục cho đến khi bước cuối cùng trong hoạt hình hoàn thành. Trong khi đó, các chức năng đồng bộ của bạn đã hoàn thành từ lâu. Nói cách khác, cuộc gọi của bạn đến animatechức năng không thực sự mất 3 giây. Hiệu ứng được mô phỏng với độ trễ và gọi lại.

Những gì bạn cần là một hàng đợi . Trong nội bộ, jQuery xếp hàng các hình động, chỉ thực hiện cuộc gọi lại của bạn sau khi hoạt ảnh tương ứng của nó hoàn thành. Nếu cuộc gọi lại của bạn sau đó bắt đầu một hình ảnh động khác, thì hiệu quả là chúng được thực hiện theo trình tự.

Trong trường hợp đơn giản nhất, điều này tương đương với những điều sau đây:

window.setTimeout(function() {
    alert("!");
    // set another timeout once the first completes
    window.setTimeout(function() {
        alert("!!");
    }, 1000);
}, 3000); // longer, but first

Đây là một chức năng lặp không đồng bộ chung. Nó sẽ gọi các hàm đã cho theo thứ tự, chờ số giây được chỉ định giữa mỗi giây.

function loop() {
    var args = arguments;
    if (args.length <= 0)
        return;
    (function chain(i) {
        if (i >= args.length || typeof args[i] !== 'function')
            return;
        window.setTimeout(function() {
            args[i]();
            chain(i + 1);
        }, 2000);
    })(0);
}    

Sử dụng:

loop(
  function() { alert("sam"); }, 
  function() { alert("sue"); });

Rõ ràng bạn có thể sửa đổi điều này để có thời gian chờ cấu hình hoặc thực hiện ngay chức năng đầu tiên hoặc dừng thực thi khi một chức năng trong chuỗi trả về falsehoặc cho applycác chức năng trong ngữ cảnh được chỉ định hoặc bất kỳ điều gì khác bạn có thể cần.


14

Tôi tin rằng thư viện async sẽ cung cấp cho bạn một cách rất thanh lịch để làm điều này. Mặc dù lời hứa và cuộc gọi lại có thể hơi khó khăn để thực hiện, async có thể đưa ra các mô hình gọn gàng để hợp lý hóa quá trình suy nghĩ của bạn. Để chạy các chức năng nối tiếp, bạn sẽ cần đặt chúng vào một thác nước không đồng bộ . Trong biệt ngữ async, mọi hàm được gọi là a taskcó một số đối số và a callback; đó là chức năng tiếp theo trong chuỗi. Cấu trúc cơ bản sẽ trông giống như:

async.waterfall([
  // A list of functions
  function(callback){
      // Function no. 1 in sequence
      callback(null, arg);
  },
  function(arg, callback){
      // Function no. 2 in sequence
      callback(null);
  }
],    
function(err, results){
   // Optional final callback will get results for all prior functions
});

Tôi vừa cố gắng giải thích ngắn gọn về cấu trúc ở đây. Đọc qua hướng dẫn thác nước để biết thêm thông tin, nó được viết khá tốt.


1
Điều này thực sự làm cho JS có thể chịu đựng hơn một chút.
mike

9

các chức năng của bạn sẽ có chức năng gọi lại, được gọi khi nó kết thúc.

function fone(callback){
...do something...
callback.apply(this,[]);

}

function ftwo(callback){
...do something...
callback.apply(this,[]);
}

sau đó sử dụng sẽ như sau:

fone(function(){
  ftwo(function(){
   ..ftwo done...
  })
});

4
asec=1000; 

setTimeout('some_3secs_function("somevalue")',asec*3);
setTimeout('some_5secs_function("somevalue")',asec*5);
setTimeout('some_8secs_function("somevalue")',asec*8);

Tôi sẽ không thảo luận sâu về setTimeout ở đây, nhưng:

  • trong trường hợp này tôi đã thêm mã để thực thi dưới dạng chuỗi. đây là cách đơn giản nhất để truyền var vào hàm setTimeout-ed của bạn, nhưng những người theo chủ nghĩa thuần túy sẽ phàn nàn.
  • bạn cũng có thể truyền tên hàm mà không có dấu ngoặc kép, nhưng không có biến nào có thể được thông qua.
  • mã của bạn không đợi setTimeout kích hoạt.
  • Điều này ban đầu có thể khiến bạn khó hiểu: vì điểm trước đó, nếu bạn chuyển một biến từ chức năng gọi của mình, biến đó sẽ không còn tồn tại nữa khi thời gian hết thời gian kích hoạt - chức năng gọi sẽ thực hiện và nó lọ đã biến mất
  • Tôi đã được biết là sử dụng các hàm ẩn danh để khắc phục tất cả điều này, nhưng cũng có thể có một cách tốt hơn,

3

Vì bạn đã gắn thẻ nó bằng javascript, tôi sẽ sử dụng điều khiển hẹn giờ vì tên hàm của bạn là 3, 5 và 8 giây. Vì vậy, hãy bắt đầu bộ hẹn giờ của bạn, 3 giây trong, gọi cuộc gọi đầu tiên, 5 giây trong cuộc gọi thứ hai, 8 giây trong cuộc gọi thứ ba, sau đó khi kết thúc, dừng bộ hẹn giờ.

Thông thường trong Javascript, những gì bạn có là chính xác cho các chức năng đang chạy lần lượt, nhưng vì có vẻ như bạn đang cố gắng thực hiện hoạt hình theo thời gian, nên bộ hẹn giờ sẽ là lựa chọn tốt nhất của bạn.


2

//sample01
(function(_){_[0]()})([
	function(){$('#art1').animate({'width':'10px'},100,this[1].bind(this))},
	function(){$('#art2').animate({'width':'10px'},100,this[2].bind(this))},
	function(){$('#art3').animate({'width':'10px'},100)},
])

//sample02
(function(_){_.next=function(){_[++_.i].apply(_,arguments)},_[_.i=0]()})([
	function(){$('#art1').animate({'width':'10px'},100,this.next)},
	function(){$('#art2').animate({'width':'10px'},100,this.next)},
	function(){$('#art3').animate({'width':'10px'},100)},
]);

//sample03
(function(_){_.next=function(){return _[++_.i].bind(_)},_[_.i=0]()})([
	function(){$('#art1').animate({'width':'10px'},100,this.next())},
	function(){$('#art2').animate({'width':'10px'},100,this.next())},
	function(){$('#art3').animate({'width':'10px'},100)},
]);


Bạn có thể vui lòng giải thích điều này là gì? Những gì được ràng buộc với gạch dưới? Hàm được gán vào nextđể làm gì?
mtso

1
Tôi giải thích mẫu 2 bằng cách sử dụng jsfiddle. jsfiddle.net/mzsteyuy/3 Nếu bạn cho phép tôi giải thích đại khái, mẫu 2 là một cách ngắn gọn của mã trong jsfiddle. gạch dưới là Mảng mà các phần tử có giá trị truy cập (i) và hàm tiếp theo và hàm [0] ~ [2].
yuuya

1

Bạn cũng có thể sử dụng lời hứa theo cách này:

    some_3secs_function(this.some_value).then(function(){
       some_5secs_function(this.some_other_value).then(function(){
          some_8secs_function(this.some_other_other_value);
       });
    });

Bạn sẽ phải làm cho some_valuetoàn cầu để truy cập nó từ bên trong .then

Ngoài ra, từ hàm bên ngoài, bạn có thể trả về giá trị mà hàm bên trong sẽ sử dụng, như vậy:

    one(some_value).then(function(return_of_one){
       two(return_of_one).then(function(return_of_two){
          three(return_of_two);
       });
    });

0

Tôi sử dụng chức năng 'WaitUntil' dựa trên setTimeout của javascript

/*
    funcCond : function to call to check whether a condition is true
    readyAction : function to call when the condition was true
    checkInterval : interval to poll <optional>
    timeout : timeout until the setTimeout should stop polling (not 100% accurate. It was accurate enough for my code, but if you need exact milliseconds, please refrain from using Date <optional>
    timeoutfunc : function to call on timeout <optional>
*/
function waitUntil(funcCond, readyAction, checkInterval, timeout, timeoutfunc) {
    if (checkInterval == null) {
        checkInterval = 100; // checkinterval of 100ms by default
    }
    var start = +new Date(); // use the + to convert it to a number immediatly
    if (timeout == null) {
        timeout = Number.POSITIVE_INFINITY; // no timeout by default
    }
    var checkFunc = function() {
        var end = +new Date(); // rough timeout estimations by default

        if (end-start > timeout) {
            if (timeoutfunc){ // if timeout function was defined
                timeoutfunc(); // call timeout function
            }
        } else {
            if(funcCond()) { // if condition was met
                readyAction(); // perform ready action function
            } else {
                setTimeout(checkFunc, checkInterval); // else re-iterate
            }
        }
    };
    checkFunc(); // start check function initially
};

Điều này sẽ hoạt động hoàn hảo nếu các chức năng của bạn đặt một điều kiện nhất định thành đúng, mà bạn có thể bỏ phiếu. Thêm vào đó, nó đi kèm với thời gian chờ, cung cấp cho bạn các lựa chọn thay thế trong trường hợp chức năng của bạn không thực hiện được điều gì đó (ngay cả trong phạm vi thời gian. Hãy suy nghĩ về phản hồi của người dùng!)

ví dụ

doSomething();
waitUntil(function() { return doSomething_value===1;}, doSomethingElse);
waitUntil(function() { return doSomethingElse_value===1;}, doSomethingUseful);

Ghi chú

Ngày gây ra ước tính thời gian chờ thô. Để có độ chính xác cao hơn, hãy chuyển sang các chức năng như console.time (). Xin lưu ý rằng Ngày cung cấp hỗ trợ chéo và trình duyệt lớn hơn. Nếu bạn không cần đo chính xác mili giây; đừng bận tâm, hoặc, thay vào đó, bọc nó và cung cấp console.time () khi trình duyệt hỗ trợ


0

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>

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.