Xử lý kim tự tháp gọi lại node.js


9

Tôi mới bắt đầu sử dụng nút và một điều tôi đã nhanh chóng nhận thấy là cách gọi lại nhanh chóng có thể tạo ra mức độ thụt lề ngớ ngẩn:

doStuff(arg1, arg2, function(err, result) {
    doMoreStuff(arg3, arg4, function(err, result) {
        doEvenMoreStuff(arg5, arg6, function(err, result) {
            omgHowDidIGetHere();
        });
    });
});

Các hướng dẫn phong cách chính thức nói với đưa từng gọi lại trong một chức năng riêng biệt, nhưng điều đó dường như quá hạn chế về việc sử dụng đóng cửa, và làm cho một đối tượng duy nhất tuyên bố ở cấp cao nhất có sẵn một vài lớp xuống, như đối tượng đã được chuyển qua tất cả các cuộc gọi lại trung gian.

Có thể sử dụng phạm vi chức năng để giúp đỡ ở đây? Đặt tất cả các hàm gọi lại cần truy cập vào một đối tượng toàn cầu bên trong một hàm khai báo đối tượng đó, để nó đi vào một bao đóng?

function topLevelFunction(globalishObject, callback) {

    function doMoreStuffImpl(err, result) {
        doMoreStuff(arg5, arg6, function(err, result) {
            callback(null, globalishObject);
        });
    }

    doStuff(arg1, arg2, doMoreStuffImpl);
}

và cứ thế cho một vài lớp nữa ...

Hoặc có các khung vv để giúp giảm mức độ thụt mà không cần khai báo một hàm được đặt tên cho mỗi lần gọi lại? Làm thế nào để bạn đối phó với kim tự tháp gọi lại?


2
Lưu ý vấn đề bổ sung với các bao đóng - trong bao đóng JS ghi lại toàn bộ bối cảnh cha mẹ (trong các ngôn ngữ khác, nó chỉ nắm bắt biến được sử dụng hoặc các biến mà người dùng yêu cầu cụ thể) gây ra một số rò rỉ bộ nhớ đẹp nếu hệ thống phân cấp gọi lại đủ sâu và, ví dụ, gọi lại được giữ lại ở đâu đó.
Eugene

Câu trả lời:


7

Promise cung cấp một sự tách biệt rõ ràng giữa các mối quan tâm giữa hành vi không đồng bộ và giao diện để các chức năng không đồng bộ có thể được gọi mà không cần gọi lại và tương tác gọi lại có thể được thực hiện trên giao diện hứa hẹn chung.

Có nhiều triển khai "lời hứa":


Ví dụ: bạn có thể viết lại cuộc gọi lại lồng nhau này

http.get(url.parse("http://test.com/"), function(res) {
    console.log(res.statusCode);
    http.get(url.parse(res.headers["location"]), function(res) {
        console.log(res.statusCode);
    });
});

giống

httpGet(url.parse("http://test.com/")).then(function (res) {
    console.log(res.statusCode);
    return httpGet(url.parse(res.headers["location"]));
}).then(function (res) {
    console.log(res.statusCode);
});

Thay vì gọi lại trong cuộc gọi lại, a(b(c()))bạn xâu chuỗi ".then" a().then(b()).then(c()).


Giới thiệu ở đây: http://howtonode.org/promises


bạn có phiền giải thích thêm về những tài nguyên này làm gì không và tại sao bạn lại đề xuất những tài nguyên này khi trả lời câu hỏi? "Câu trả lời chỉ liên kết" không được chào đón tại Stack Exchange
gnat

1
Được rồi xin lỗi. Tôi đã thêm một ví dụ và nhiều thông tin hơn.
Fabien Sa

3

Thay thế cho lời hứa, bạn nên xem yieldtừ khóa kết hợp với các hàm tạo sẽ được giới thiệu trong EcmaScript 6. Cả hai đều có sẵn trong các bản dựng Node.js 0.11.x, nhưng yêu cầu bạn phải chỉ định thêm --harmonycờ khi chạy Node .js:

$ node --harmony app.js

Sử dụng các cấu trúc đó và một thư viện như đồng của TJ Holowaychuk cho phép bạn viết mã không đồng bộ theo kiểu trông giống như mã đồng bộ, mặc dù nó vẫn chạy theo cách không đồng bộ. Về cơ bản, những điều này cùng nhau thực hiện hỗ trợ đồng quy cho Node.js.

Về cơ bản, những gì bạn cần làm là viết hàm tạo cho mã chạy nội dung không đồng bộ, gọi nội dung không đồng bộ trong đó, nhưng đặt tiền tố cho yieldtừ khóa. Vì vậy, cuối cùng mã của bạn trông giống như:

var run = function * () {
  var data = yield doSomethingAsync();
  console.log(data);
};

Để chạy chức năng tạo này, bạn cần một thư viện như đồng đã đề cập trước đó. Cuộc gọi sau đó trông giống như:

co(run);

Hoặc, để đặt nó nội tuyến:

co(function * () {
  // ...
});

Xin lưu ý rằng từ các chức năng của trình tạo, bạn có thể gọi các hàm tạo khác, tất cả những gì bạn cần làm là thêm tiền tố vào chúng yieldmột lần nữa.

Để biết giới thiệu về chủ đề này, hãy tìm kiếm trên Google các cụm từ như yield generators es6 async nodejsvà bạn sẽ tìm thấy vô số thông tin. Phải mất một thời gian để làm quen với nó, nhưng một khi bạn đã nhận được nó, bạn không muốn quay lại bao giờ.

Xin lưu ý rằng điều này không chỉ cung cấp cho bạn một cú pháp đẹp hơn để gọi các hàm, nó còn cho phép bạn sử dụng các công cụ logic luồng điều khiển (đồng bộ) thông thường, như forcác vòng lặp hoặc try/ catch. Không còn phải loay hoay với rất nhiều cuộc gọi lại và tất cả những điều này.

Chúc may mắn và vui vẻ :-)!


0

Bây giờ bạn có gói asyncawait , với cú pháp rất gần với hỗ trợ gốc trong await& asynctrong Node trong tương lai .

Về cơ bản, nó cho phép bạn viết mã không đồng bộ trông đồng bộ , giảm đáng kể mức LỘC & thụt lề, với sự đánh đổi của một sự mất hiệu suất nhẹ (số chủ sở hữu gói là tốc độ 79% so với các cuộc gọi lại thô), hy vọng sẽ giảm khi có hỗ trợ gốc.

Vẫn là một lựa chọn tốt để thoát khỏi cơn ác mộng gọi lại / kim tự tháp của cơn ác mộng IMO, khi hiệu suất không phải là mối quan tâm chính & nơi phong cách viết đồng bộ phù hợp hơn với nhu cầu của dự án của bạn.

Ví dụ cơ bản từ gói doc:

var foo = async (function() {
    var resultA = await (firstAsyncCall());
    var resultB = await (secondAsyncCallUsing(resultA));
    var resultC = await (thirdAsyncCallUsing(resultB));
    return doSomethingWith(resultC);
});
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.