Cả Observables và Luồ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 Observer
và Observable
.
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 push
trên một Readable
dòng suối và write
trên Writable
mặ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 Observable
với Readable
luồng và Observer
với Writable
luồ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 Observer
và Subscription
, đượ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, Subscription
cho 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
, Transform
và Writable
suối nhưng ân API subclassing thay vì chaining được kết Readable
s và áp dụng chức năng này. Observable
Ví 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() {
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à Observable
khá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.