Luồng Node.js

Tìm hiểu xem luồng dùng để làm gì, tại sao chúng lại quan trọng và cách sử dụng chúng.

Luồng là gì

Luồng là một trong những khái niệm cơ bản cung cấp năng lượng cho các ứng dụng Node.js.

Chúng là một cách để xử lý việc đọc / ghi tệp, truyền thông mạng hoặc bất kỳ loại trao đổi thông tin đầu cuối nào một cách hiệu quả.

Luồng không phải là một khái niệm dành riêng cho Node.js. Chúng đã được giới thiệu trong hệ điều hành Unix nhiều thập kỷ trước và các chương trình có thể tương tác với nhau qua các luồng thông qua toán tử đường ống (|).

Ví dụ, theo cách truyền thống, khi bạn yêu cầu chương trình đọc một tệp, tệp đó sẽ được đọc vào bộ nhớ, từ đầu đến cuối và sau đó bạn xử lý nó.

Sử dụng các luồng bạn đọc từng đoạn một, xử lý nội dung của nó mà không cần lưu tất cả vào bộ nhớ.

Node.jsstreammô-đuncung cấp nền tảng mà trên đó tất cả các API phát trực tuyến được xây dựng.

Tại sao lại phát trực tiếp

Về cơ bản, các luồng cung cấp hai ưu điểm chính bằng cách sử dụng các phương pháp xử lý dữ liệu khác:

  • Hiệu quả bộ nhớ: bạn không cần phải tải một lượng lớn dữ liệu vào bộ nhớ trước khi có thể xử lý nó
  • Hiệu quả về thời gian: mất ít thời gian hơn để bắt đầu xử lý dữ liệu ngay khi bạn có, thay vì đợi cho đến khi toàn bộ tải trọng dữ liệu có sẵn để bắt đầu

Ví dụ về luồng

Một ví dụ điển hình là đọc tệp từ đĩa.

Sử dụng nútfsbạn có thể đọc tệp và phân phát tệp đó qua HTTP khi kết nối mới được thiết lập với máy chủ http của bạn:

const http = require('http')
const fs = require('fs')

const server = http.createServer(function (req, res) { fs.readFile(__dirname + ‘/data.txt’, (err, data) => { res.end(data) }) }) server.listen(3000)

readFile()đọc toàn bộ nội dung của tệp và gọi hàm gọi lại khi hoàn tất.

res.end(data)trong lệnh gọi lại sẽ trả về nội dung tệp cho máy khách HTTP.

Nếu tập tin lớn, thao tác sẽ mất khá nhiều thời gian. Đây là điều tương tự được viết bằng luồng:

const http = require('http')
const fs = require('fs')

const server = http.createServer((req, res) => { const stream = fs.createReadStream(__dirname + ‘/data.txt’) stream.pipe(res) }) server.listen(3000)

Thay vì đợi cho đến khi tệp được đọc hoàn toàn, chúng tôi bắt đầu phát trực tuyến nó đến máy khách HTTP ngay khi chúng tôi có một phần dữ liệu sẵn sàng được gửi.

ống()

Ví dụ trên sử dụng dòngstream.pipe(res): cácpipe()phương thức được gọi trên dòng tệp.

Mã này làm gì? Nó lấy nguồn và đưa nó vào đích.

Bạn gọi nó trên luồng nguồn, vì vậy trong trường hợp này, luồng tệp được chuyển tới phản hồi HTTP.

Giá trị trả về củapipe()phương thức là luồng đích, đây là một điều rất thuận tiện cho phép chúng tôi xâu chuỗi nhiềupipe()các cuộc gọi, như thế này:

src.pipe(dest1).pipe(dest2)

Cấu trúc này giống như làm

src.pipe(dest1)
dest1.pipe(dest2)

API Node hỗ trợ luồng

Do những ưu điểm của chúng, nhiều mô-đun cốt lõi của Node.js cung cấp khả năng xử lý luồng gốc, đáng chú ý nhất là:

  • process.stdintrả về một luồng được kết nối với stdin
  • process.stdouttrả về một luồng được kết nối với stdout
  • process.stderrtrả về một luồng được kết nối với stderr
  • fs.createReadStream()tạo một luồng có thể đọc được vào một tệp
  • fs.createWriteStream()tạo một luồng có thể ghi vào một tệp
  • net.connect()bắt đầu kết nối dựa trên luồng
  • http.request()trả về một thể hiện của lớp http.ClientRequest, là một luồng có thể ghi
  • zlib.createGzip()nén dữ liệu bằng gzip (một thuật toán nén) vào một luồng
  • zlib.createGunzip()giải nén một dòng gzip.
  • zlib.createDeflate()nén dữ liệu bằng cách sử dụng deflate (một thuật toán nén) vào một luồng
  • zlib.createInflate()giải nén một dòng xì hơi

Các loại luồng khác nhau

Có bốn lớp luồng:

  • Readable: một luồng bạn có thể chuyển từ đó, nhưng không thể chuyển vào (bạn có thể nhận dữ liệu, nhưng không thể gửi dữ liệu đến luồng đó). Khi bạn đẩy dữ liệu vào một luồng có thể đọc được, nó sẽ được lưu vào bộ đệm, cho đến khi người tiêu dùng bắt đầu đọc dữ liệu.
  • Writable: một luồng bạn có thể chuyển vào, nhưng không thể chuyển từ (bạn có thể gửi dữ liệu, nhưng không thể nhận từ nó)
  • Duplex: một luồng mà bạn có thể chuyển cả vào và chuyển từ đó, về cơ bản là sự kết hợp giữa luồng Có thể đọc và Ghi được
  • Transform: một luồng Chuyển đổi tương tự như một song công, nhưng đầu ra là một biến đổi của đầu vào của nó

Cách tạo luồng có thể đọc được

Chúng tôi nhận được luồng Có thể đọc từstreammô-đunvà chúng tôi khởi tạo nó

const Stream = require('stream')
const readableStream = new Stream.Readable()

Bây giờ luồng đã được khởi tạo, chúng tôi có thể gửi dữ liệu đến luồng đó:

readableStream.push('hi!')
readableStream.push('ho!')

Cách tạo luồng có thể ghi

Để tạo một luồng có thể ghi, chúng tôi mở rộng cơ sởWritablevà chúng tôi triển khai phương thức _write () của nó.

Đầu tiên hãy tạo một đối tượng luồng:

const Stream = require('stream')
const writableStream = new Stream.Writable()

sau đó thực hiện_write:

writableStream._write = (chunk, encoding, next) => {
    console.log(chunk.toString())
    next()
}

Giờ đây, bạn có thể truyền một luồng có thể đọc được trong:

process.stdin.pipe(writableStream)

Cách lấy dữ liệu từ luồng có thể đọc được

Làm cách nào để chúng tôi đọc dữ liệu từ một luồng có thể đọc được? Sử dụng luồng có thể ghi:

const Stream = require('stream')

const readableStream = new Stream.Readable() const writableStream = new Stream.Writable()

writableStream._write = (chunk, encoding, next) => { console.log(chunk.toString()) next() }

readableStream.pipe(writableStream)

readableStream.push(‘hi!’) readableStream.push(‘ho!’)

Bạn cũng có thể xem trực tiếp một luồng có thể đọc được bằng cách sử dụngreadablebiến cố:

readableStream.on('readable', () => {
  console.log(readableStream.read())
})

Cách gửi dữ liệu đến luồng có thể ghi

Sử dụng luồngwrite()phương pháp:

writableStream.write('hey!\n')

Báo hiệu luồng có thể ghi rằng bạn đã kết thúc quá trình viết

Sử dụngend()phương pháp:

const Stream = require('stream')

const readableStream = new Stream.Readable() const writableStream = new Stream.Writable()

writableStream._write = (chunk, encoding, next) => { console.log(chunk.toString()) next() }

readableStream.pipe(writableStream)

readableStream.push(‘hi!’) readableStream.push(‘ho!’)

writableStream.end()

Tải xuống miễn phí của tôiSổ tay Node.js


Các hướng dẫn nút khác: