Cuộc gọi lại bị trì hoãn là gì?


15

Tôi hiểu ý tưởng của một cuộc gọi lại, nơi tôi chuyển một chức năng sang một chức năng khác và sau đó chức năng đó sẽ sử dụng chức năng được cung cấp theo ý muốn.

Tôi đang vật lộn để hiểu các cuộc gọi lại bị trì hoãn, ngay cả sau khi googling nó.

Ai đó có thể cung cấp một lời giải thích đơn giản xin vui lòng? Tôi lập trình trong Ruby, nhưng cũng biết C / C ++ một chút, nhưng hầu hết tôi là một lập trình viên ngôn ngữ lắp ráp có kinh nghiệm. Vì vậy, tôi tự hỏi là nó hơi giống như một chồng các địa chỉ gọi lại được pop'd? Tôi hy vọng tìm hiểu jquery hoặc node.js và các cuộc gọi lại bị trì hoãn này dường như không thể thiếu đối với cả hai. Tôi hiểu các nguyên tắc phân luồng cơ bản (mặc dù đối tượng mutex làm cho đầu tôi đau;)


Ý bạn là Deferredcác đối tượng của jQuery ? Đây có phải là một cái gì đó cụ thể cho Node.js?
bfavaretto

1
Không, ý tôi là nói chung. Mặc dù tôi muốn học jquery và có thể là node.js, tôi cảm thấy rằng tôi cần phải xử lý những gì một cuộc gọi lại bị trì hoãn thực sự là lần đầu tiên. Tôi đã đọc bài viết trên Wikipedia về các cuộc gọi lại, nhưng tôi không thể hiểu được các cuộc gọi lại bị trì hoãn, có vẻ như nội tại của mô hình hoạt động không đồng bộ sẽ liên quan đến các ngôn ngữ sử dụng nó.


1
Tôi thực sự yêu cầu ý tưởng khái niệm về cuộc gọi lại bị trì hoãn trái ngược với triển khai của họ - xin lỗi nếu tôi không làm rõ hơn điều đó. Tôi đã đưa ra các ví dụ ngôn ngữ nhiều hơn để giải thích ý tưởng mà tôi đang cố gắng làm rõ và cũng là nền tảng lập trình của tôi để mọi người biết cách đưa ra câu trả lời. Cảm ơn rất nhiều cho câu trả lời cho đến nay - tôi đang đến đó!
gian

Ok tôi nghĩ rằng tôi đã có nó bây giờ folks, cảm ơn tất cả các bạn! Tôi không biết làm thế nào để trả lời mặc dù. Cameron giải thích khái niệm này một cách đơn giản nhất và đó là những gì tôi thực sự theo đuổi, nhưng những người khác cũng theo đuổi và bổ sung vào kiến ​​thức của tôi. Tôi không chắc chắn cách nào để chấp nhận câu trả lời vì tôi chưa quen với điều này;)
dự kiến

Câu trả lời:


4

Theo yêu cầu, đây là ý kiến ​​trình bày như một câu trả lời:


Tôi không chắc chắn bạn hoàn toàn hiểu được rằng các hàm trong JS là các đối tượng hạng nhất và do đó có thể được lưu trữ cho đến khi cần, qua thời gian chúng được tạo.

Ví dụ: giả sử bạn muốn ghi vào một tệp, sau đó in ra một thông điệp tường trình; vì vậy bạn gọi hàm "write ()" (hoặc bất cứ thứ gì) và truyền cho nó một hàm xuất thông điệp tường trình (đây là hàm gọi lại bị trì hoãn). "write ()" lưu trữ bên trong một tham chiếu đến hàm đã cho, bắt đầu ghi vào tệp và thiết lập cuộc gọi lại của chính nó để biết khi nào việc ghi kết thúc. Sau đó nó trả về trước khi viết xong; khi đó, cuộc gọi lại nội bộ được gọi bằng cách nào đó (đây là công việc của khung cơ bản - trong trường hợp của node.js, nó được thực hiện với một vòng lặp sự kiện), sau đó gọi cuộc gọi lại của bạn để in thông điệp tường trình.

Phần "hoãn lại" đơn giản có nghĩa là chức năng gọi lại của bạn không được gọi ngay lập tức; gọi nó được hoãn lại cho đến thời điểm thích hợp. Trong trường hợp các hàm không đồng bộ giống như nhiều hàm trong node.js, cuộc gọi lại đã cho thường được gọi khi hoạt động hoàn tất (hoặc xảy ra lỗi).

Hầu hết mọi thứ đều không đồng bộ trong node.js, nhưng trong trình duyệt với jQuery, hầu hết mọi thứ thực sự đồng bộ (ngoại trừ, rõ ràng, đối với các yêu cầu AJAX). Vì các hàm hạng nhất rất tiện dụng trong JavaScript (đặc biệt là vì hỗ trợ đóng tuyệt vời), các cuộc gọi lại cũng được sử dụng ở mọi nơi trong trình duyệt, nhưng chúng không được "hoãn lại" cho các hoạt động đồng bộ (trừ khi chúng không được gọi ngay lập tức bạn, nhưng sau này bởi chức năng bạn gọi).

Thực tế là hệ thống cơ bản được điều khiển theo sự kiện là trực giao với việc sử dụng các cuộc gọi lại bị trì hoãn; bạn có thể tưởng tượng một phiên bản (rất chậm) của node.js đã bắt đầu một luồng cho mọi hoạt động và sau đó gọi lại cuộc gọi lại đã cho khi luồng hoàn thành công việc của nó, mà không sử dụng các sự kiện nào cả. Tất nhiên, đây là một mô hình khủng khiếp, nhưng nó minh họa quan điểm của tôi :-)


8

Cách thức gọi lại bị trì hoãn hoạt động là mỗi khi bạn thêm một cuộc gọi lại vào nó, cuộc gọi lại đó được đẩy đến một mảng. Sau đó, khi phương thức .resolve()hoặc .resolveWith()được gọi trên đối tượng bị trì hoãn, tất cả các .done()cuộc gọi lại trong mảng được thực hiện theo thứ tự.

Bây giờ chúng ta có thể xem đối tượng Trì hoãn là gì. Lấy đoạn trích dưới đây làm ví dụ.

var deferred = $.Deferred();
var promise = deferred.promise();

Những gì chúng ta có bây giờ là một đối tượng bị trì hoãn và đối tượng hứa hẹn của đối tượng bị trì hoãn. Các đối tượng thu nhập hoãn lại có tất cả các phương pháp tương tự như các đối tượng hứa hẹn, tuy nhiên đối tượng hứa hẹn chỉ có phương pháp .done(), .fail().always()được sử dụng để thêm callbacks đến đối tượng trì hoãn cho mỗi tương ứng event. Mặt khác, đối tượng hoãn lại có một số phương thức khác, quan trọng nhất là .resolve().reject(). Khi các phương thức này được gọi trên đối tượng hoãn lại, tất cả các cuộc gọi lại được gọi. .resolve()kích hoạt .done().always()gọi lại trong khi .reject()phương thức gọi .fail().always()gọi lại.

Nói chung, đối tượng hoãn lại được giữ trong một phạm vi riêng tư và đối tượng lời hứa được trả về từ hàm để có thể đặt lại các cuộc gọi lại trên nó. Đối tượng bị trì hoãn sẽ được giải quyết sau đó, chẳng hạn như sau khi yêu cầu ajax hoàn thành hoặc sau khi hình ảnh được tải, sau khi setTimeout, v.v. Điều quan trọng là phải nhận ra rằng một đối tượng bị trì hoãn chỉ có thể được giải quyết một lần. Nếu nó đã được giải quyết, các cuộc gọi lại sẽ được gọi ngay lập tức.

Đây là một ví dụ khác, một ví dụ mà tôi sử dụng:

function loadImage(url) {
    var def = $.Deferred(),
        img = new Image();
    $(img).on("load error",function(e){
        if (e.type === "error") {
            def.reject(url);
        }
        else {
            def.resolve(url);
        }
    });
    img.src = url;
    // return the promise object so that callbacks can
    // be defined on the deferred object.
    return def.promise();
}
loadImage("foobar.jpg").done(function(){
    alert("The image is loaded!");
}).fail(function(){
    alert("The image failed to load!");
}).always(function(){
    alert("This is always called!");
});

Để biết thêm thông tin về $.Deferred()phương thức của jQuery và các đối tượng bị trì hoãn, hãy truy cập http://api.jquery.com/carget/deferred-object/


Điều này có lẽ sẽ là vô giá khi tôi hiểu được khái niệm về một cuộc gọi lại bị trì hoãn. Xin lỗi, nhưng tôi vẫn không hiểu khái niệm về cuộc gọi lại bị trì hoãn là gì. Tôi đang tìm kiếm ý tưởng khái niệm đằng sau nó. Sắp xếp theo ý tưởng của Mihia. Một khi tôi có thể có được vòng đầu của mình thì có lẽ tôi có thể hiểu js.
gian

3

Tôi không chắc chắn nhưng tôi tin rằng một cuộc gọi lại bị từ chối đề cập đến một cuộc gọi lại không đồng bộ, vì vậy bạn sẽ gặp may mắn hơn khi tìm kiếm điều đó.

Giải thích tốt nhất tôi tìm thấy là tại http://www.nodebeginner.org

Xin chào, có lẽ làExpensiveFunction (), vui lòng thực hiện công cụ của bạn, nhưng tôi, luồng Node.js duy nhất, sẽ không đợi ở đây cho đến khi bạn kết thúc, tôi sẽ tiếp tục thực thi các dòng mã bên dưới bạn, vì vậy bạn vui lòng lấy hàm gọi lại này () ở đây và gọi nó khi bạn hoàn thành công việc đắt tiền của mình? Cảm ơn!"

Trong ví dụ này, có lẽExpensiveFunction là một chức năng không chặn (hoặc async). Điều này có nghĩa là nó không được thực thi ngay mà được đặt trong một vòng lặp sự kiện. Chuỗi node.js sẽ tiếp tục thực thi, nhưng tại một số thời điểm, nó sẽ quyết định thực hiện một cái gì đó từ vòng lặp sự kiện. Khi nó đạt tới có lẽExpensiveFunction, nó gọi nó và khi có lẽExpensiveFunction kết thúc thực thi, nó gọi hàm gọi lại (hoãn lại) được truyền dưới dạng tham số cho nó.

Như một ví dụ về có lẽ làExpensiveFunction, bạn có thể lấy fs.readFile


Cảm ơn. Nhưng, chức năng đắt tiền sẽ hoạt động như thế nào nếu nó đã trở lại và bạn quay lại chuỗi xử lý chính? Tôi không nhận được chút đó. Bạn đang nói chức năng vẫn tồn tại theo một cách nào đó sau khi nó kết thúc?

Chỉnh sửa câu trả lời, có lẽ bây giờ nó rõ ràng hơn.

Rất hữu ích. Nhưng ... tôi có phải xử lý mảng gọi lại được lưu trữ này không? Ý tôi là, cái gì đang xử lý danh sách này. Có phải (ví dụ) js nâng các cuộc gọi lại này trong nền mà bạn không phải làm gì về nó, hoặc đó là một sự kiện gây ra node.js hoặc một cái gì đó để gọi một cuộc gọi lại cụ thể. Xin lỗi, tôi đang nhận được khoảng 70% những gì bạn đang nói nhưng tôi chỉ mất một chút về phần còn lại :)
gian

@tentimes - "cái gì đang xử lý danh sách này", đối tượng $ .Deferred () đang xử lý danh sách. Khi bạn gọi .resolve()hoặc .reject()trên đối tượng hoãn lại ban đầu, danh sách các cuộc gọi lại được gọi.
dùng400654

2
@tentimes: Từ những gì bạn đang nói, tôi không chắc bạn hoàn toàn hiểu được thực tế rằng các hàm trong JS là các đối tượng hạng nhất và do đó có thể được lưu trữ cho đến khi cần, qua thời gian chúng được tạo. Ví dụ: bạn muốn ghi vào một tệp, sau đó in ra một thông điệp tường trình; vì vậy bạn gọi hàm "write" (hoặc bất cứ thứ gì) và truyền cho nó một hàm xuất thông điệp tường trình. "write ()" lưu trữ bên trong một tham chiếu đến hàm đã cho, bắt đầu ghi vào tệp và thiết lập cuộc gọi lại của chính nó để biết khi nào việc ghi kết thúc. Sau đó nó trả về trước khi viết xong; khi đó, chức năng của bạn được gọi.
Cameron

3

JavaScript là một luồng đơn, vì vậy bạn không thể nghĩ về các luồng để hiểu điều này. Dưới đây là một ví dụ về cả các cuộc gọi lại thông thường và không đồng bộ khi sử dụng jQuery:

var regularCallback = function(evt) {
    alert("I'm a callback!")
}
var asyncCallback = function(data) {
    alert("I only run when an async operation finishes!")
}

// Bind the regular callback to a button's click event
$('#mybutton').on('click', regularCallback);

// Start an ajax request to the server. The request is asynchronous, so code
// below this line will execute immediately. The callback function
// will only be called when the request is complete.
$.get("http://google.com", asyncCallback);

Bây giờ điều này sẽ đưa tôi đến một nơi nào đó, cảm ơn. Vì vậy, sự không đồng bộ là để đáp ứng với một sự kiện mà tôi đã thiết lập với ajax - chỉ cần trang trại và nếu sự kiện đó xảy ra, cuộc gọi lại của tôi có được gọi không? Tôi nghĩ rằng tôi đang nhận được nó. Liệu node.js / jquery có làm điều gì đó tương tự với các đối tượng trì hoãn jquery usnig và một hệ thống tương tác phức tạp với chúng để thực sự chỉ làm điều tương tự nhưng sử dụng các phương thức?
gian

1
@tentimes, Chính xác! Các cuộc gọi lại thường chạy để đáp ứng với các sự kiện (nhưng vì "gọi lại" không phải là cấu trúc ngôn ngữ js, đôi khi thuật ngữ này được sử dụng trong các ngữ cảnh khác). Các đối tượng bị trì hoãn (lời hứa) mà bạn thấy trong câu trả lời của Kevin về cơ bản là đường cú pháp. Họ nhận được "giải quyết" hoặc "bị từ chối" khi một số sự kiện (không đồng bộ) được kích hoạt, sau đó họ gọi cuộc gọi lại thích hợp ("thực hiện" hoặc "không thành công", sau đó "luôn luôn"). Sử dụng chúng trong jQuery có thể làm cho mã dễ đọc hơn và cho phép một số thủ thuật bổ sung (như dễ dàng thêm một cuộc gọi lại thứ hai sau khi thực hiện yêu cầu ajax chẳng hạn).
bfavaretto

Cả hai đều là cuộc gọi lại không đồng bộ
Raynos

1

Cuộc gọi lại bị trì hoãn (còn gọi là Lời hứa ) cho phép bạn viết mã không đồng bộ tuần tự, không bị đau và gọi lại spaghetti:

$.when( doAjax(), doAnotherAjax() ).then( haveFunWithMoreAjax ).then( animateStuff );

'khi' cho phép bạn chờ các chức năng hoạt động song song và thencó thể được nối tiếp nhau.

một lưu ý: jQuery hoãn lại ! = Promices / A , cú pháp của chúng hơi khác một chút.

Có nhiều bài viết hay về chủ đề: một bài tại IEBlog và bài khác trong một số blog ngẫu nhiên , một cuốn sách và một câu hỏi stackoverflow phổ biến


1
uhh .. hóa ra - OP hỏi điều hơi khác một chút .. tốt, hãy hy vọng câu trả lời này sẽ giúp người khác, một ngày nào đó, có thể.
c69
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.