Node.js Streams so với Observables


77

Sau khi tìm hiểu về quan sát , tôi thấy họ khá giống với Node.js suối . Cả hai đều có cơ chế thông báo cho người tiêu dùng bất cứ khi nào dữ liệu mới đến, xảy ra lỗi hoặc không có thêm dữ liệu (EOF).

Tôi rất thích tìm hiểu về sự khác biệt về khái niệm / chức năng giữa hai loại. Cảm ơn!


1
@BenjaminGruenbaum Tôi tự hỏi tại sao bạn lại gắn thẻ này với rxjs và thịt xông khói? OP dường như đề cập đến những điều có thể quan sát được từ ecmascript-hài hòa
Bergi

@Bergi kiến ​​thức trước về OP và câu hỏi. Về cơ bản.
Benjamin Gruenbaum

1
Xin chúc mừng vì số phiếu ủng hộ, nhưng tôi không biết tại sao câu hỏi này không được kết thúc. Làm thế nào đây là một câu hỏi thực sự / thích hợp cho SO.
Alexander Mills

4
@AlexanderMills làm thế nào đây không phải là một câu hỏi thích hợp cho SO? Đây không phải là câu hỏi "đâu là câu hỏi yêu thích của bạn"; nó yêu cầu sự khác biệt giữa hai mẫu phản ứng thường được sử dụng trong JS / Node.
Michael Martin-Smucker

Câu trả lời:


101

Cả ObservablesLuồng của node.js đều cho phép bạn giải quyết cùng một vấn đề cơ bản: xử lý không đồng bộ một chuỗi giá trị. Tôi tin rằng sự khác biệt chính giữa cả hai có liên quan đến bối cảnh đã thúc đẩy sự xuất hiện của nó. Bối cảnh đó được phản ánh trong thuật ngữ và API.

Về phía Observables , bạn có một phần mở rộng cho EcmaScript giới thiệu mô hình lập trình phản ứng. Nó cố gắng lấp đầy khoảng cách giữa việc tạo ra giá trị và tính không đồng bộ bằng các khái niệm tối giản và có thể kết hợp của ObserverObservable.

Về phía node.js và Streams , bạn muốn tạo một giao diện để xử lý không đồng bộ và hiệu quả các luồng mạng và tệp cục bộ. Xuất phát từ thuật ngữ mà bối cảnh ban đầu và bạn nhận được pipe, chunk, encoding, flush, Duplex, Buffer, vv Bởi có một cách tiếp cận thực dụng mà cung cấp hỗ trợ rõ ràng cho trường hợp sử dụng cụ thể bạn mất một số khả năng điều soạn vì nó không phải là thống nhất. Ví dụ, bạn sử dụng pushtrên một Readabledòng suối và writetrên Writablemặc dù, về mặt khái niệm, bạn đang làm điều tương tự: công bố một giá trị.

Vì vậy, trong thực tế, nếu bạn xem xét các khái niệm và nếu bạn sử dụng tùy chọn { objectMode: true }, bạn có thể khớp Observablevới Readableluồng và Observervới Writableluồng. Bạn thậm chí có thể tạo một số bộ điều hợp đơn giản giữa hai mô hình.

var Readable = require('stream').Readable;
var Writable = require('stream').Writable;
var util = require('util');

var Observable = function(subscriber) {
    this.subscribe = subscriber;
}

var Subscription = function(unsubscribe) {
    this.unsubscribe = unsubscribe;
}

Observable.fromReadable = function(readable) {
    return new Observable(function(observer) {
        function nop() {};

        var nextFn = observer.next ? observer.next.bind(observer) : nop;
        var returnFn = observer.return ? observer.return.bind(observer) : nop;
        var throwFn = observer.throw ? observer.throw.bind(observer) : nop;

        readable.on('data', nextFn);
        readable.on('end', returnFn);
        readable.on('error', throwFn);

        return new Subscription(function() {
            readable.removeListener('data', nextFn);
            readable.removeListener('end', returnFn);
            readable.removeListener('error', throwFn);
        });
    });
}

var Observer = function(handlers) {
    function nop() {};

    this.next = handlers.next || nop;
    this.return = handlers.return || nop;
    this.throw = handlers.throw || nop;
}

Observer.fromWritable = function(writable, shouldEnd, throwFn) {
    return new Observer({
        next: writable.write.bind(writable), 
        return: shouldEnd ? writable.end.bind(writable) : function() {}, 
        throw: throwFn
    });
}

Bạn có thể nhận thấy rằng tôi đã thay đổi một vài tên và sử dụng các khái niệm đơn giản hơn ObserverSubscription, được giới thiệu ở đây, để tránh quá tải các trách nhiệm được thực hiện bởi Observables trong Generator. Về cơ bản, Subscriptioncho phép bạn hủy đăng ký khỏi Observable. Dù sao, với đoạn mã trên, bạn có thể có một pipe.

Observable.fromReadable(process.stdin).subscribe(Observer.fromWritable(process.stdout));

So với process.stdin.pipe(process.stdout), những gì bạn có là cách kết hợp, lọc và chuyển đổi các luồng cũng hoạt động cho bất kỳ chuỗi dữ liệu nào khác. Bạn có thể đạt được điều đó với Readable, TransformWritablesuối nhưng ân API subclassing thay vì chaining được kết Readables và áp dụng chức năng này. ObservableVí dụ, trên mô hình, các giá trị biến đổi tương ứng với việc áp dụng một hàm biến áp cho dòng. Nó không yêu cầu loại con mới của Transform.

Observable.just = function(/*... arguments*/) {
    var values = arguments;
    return new Observable(function(observer) {
        [].forEach.call(values, function(value) {
            observer.next(value);
        });
        observer.return();
        return new Subscription(function() {});
    });
};

Observable.prototype.transform = function(transformer) {
    var source = this;
    return new Observable(function(observer) {
        return source.subscribe({
            next: function(v) {
                observer.next(transformer(v));
            },
            return: observer.return.bind(observer),
            throw: observer.throw.bind(observer)
        });
    });
};

Observable.just(1, 2, 3, 4, 5).transform(JSON.stringify)
  .subscribe(Observer.fromWritable(process.stdout))

Kết luận? Thật dễ dàng để giới thiệu mô hình phản ứng và Observablekhái niệm ở bất cứ đâu. Khó hơn để triển khai toàn bộ thư viện xung quanh khái niệm đó. Tất cả những chức năng nhỏ đó cần phải hoạt động cùng nhau một cách nhất quán. Rốt cuộc, dự án ReactiveX vẫn đang tiếp tục. Nhưng nếu bạn thực sự cần gửi nội dung tệp cho máy khách, hãy xử lý mã hóa và nén nó thì hỗ trợ ở đó, trong NodeJS, và nó hoạt động khá tốt.


5
Tôi thực sự không chắc chắn về toàn bộ "phần mở rộng cho điều Ecmascript". RxJS chỉ là một thư viện, giống với RxJava, v.v. Cuối cùng, trong ES7 hoặc ES8 có thể có một số từ khóa trong ES / JS liên quan đến Observables, nhưng chúng chắc chắn không phải là một phần của ngôn ngữ và chắc chắn không phải khi bạn trả lời câu hỏi vào năm 2015.
Alexander Mills

1
Việc triển khai RX có hỗ trợ áp suất ngược không mất dữ liệu không? Ví dụ: nếu nodejs đọc luồng ở chế độ bị tạm dừng thì chúng ta có thể sử dụng read()phương pháp để đọc từ luồng theo yêu cầu. Và drain eventcó thể báo hiệu rằng luồng có thể ghi có thể nhận được nhiều dữ liệu hơn.
Buggy
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.