1. مقدمة
تقدم هذه المقالة الطرق الأساسية لتطوير البرامج باستخدام تدفقات Node.js.
<code> "يجب أن يكون لدينا بعض الطرق لتوصيل البرامج مثل خرطوم الحديقة-المقطع غير المتدني عندما يصبح من الضروري تدليك البيانات غير المنتهية. هذه هي طريق IO أيضًا." 11 أكتوبر 1964 </code>
كان أول من يتلامس مع تيار هو عقود الممارسة التي بدأت مع UNIX المبكر ، والتي أثبتت أن أفكار التيار يمكنها ببساطة تطوير بعض الأنظمة الضخمة. في UNIX ، يتم تنفيذ الدفق من خلال | ؛ في العقدة ، كوحدة دفق مدمجة ، يتم استخدام العديد من الوحدات الأساسية والوحدات النمطية ثلاثية الحزبية. مثل UNIX ، فإن التشغيل الرئيسي لتيار العقدة هو .pipe () ، ويمكن للمستخدمين استخدام آلية الضغط المضاد للتحكم في التوازن بين القراءة والكتابة.
يمكن أن يوفر الدفق للمطورين واجهة موحدة يمكنها إعادة استخدام التوازن والتحكم في القراءة والكتابة بين التدفقات من خلال واجهات الدفق المجردة.
2. لماذا استخدم الدفق
I/O in Node غير متزامن ، لذا فإن القراءة والكتابة على القرص والشبكة تتطلب القراءة والكتابة إلى البيانات من خلال وظائف رد الاتصال. فيما يلي رمز بسيط لخادم تنزيل الملفات:
<code> var http = require ('http') ؛ var fs = require ('fs') ؛ var server = http.createserver (function (req ، res) {fs.readfile (__ dirname + '/data.txt' ، function (err ، data) {res.end (data) ؛}}) ؛يمكن لهذه الرموز تنفيذ الوظائف المطلوبة ، لكن الخدمة تحتاج إلى ذاكرة التخزين المؤقت لبيانات الملف بأكملها إلى الذاكرة قبل إرسال بيانات الملف. إذا كان ملف "data.txt" كبيرًا وكان التزامن كبيرًا ، فسيتم إهدار الكثير من الذاكرة. نظرًا لأن المستخدم يحتاج إلى الانتظار حتى يتم تخزين الملف بالكامل إلى الذاكرة قبل قبول بيانات الملف ، فإن هذا يؤدي إلى تجربة مستخدم سيئة للغاية. لحسن الحظ ، كل من المعلمات (REQ ، RES) هي دفق ، حتى نتمكن من استخدام fs.creeateadstream () بدلاً من fs.readfile ():
<code> var http = require ('http') ؛ var fs = require ('fs') ؛ var server = http.createserver (function (req ، res) {var dream = fs.createadstream (__ dirname + '/dtex.تستمع طريقة .pipe () إلى أحداث "البيانات" و "النهاية" لـ Fs.CreeCreeDstream () ، بحيث لا يحتاج ملف "data.txt" إلى تخزين الملف بأكمله. بعد اكتمال اتصال العميل ، يمكن إرسال كتلة بيانات إلى العميل على الفور. فائدة أخرى من استخدام .pipe () هي أنه يمكن حل مشكلة القراءة والكتابة الناتجة عن زمن انتقال العميل الكبير للغاية. إذا كنت ترغب في ضغط الملف وإرساله ، فيمكنك استخدام وحدة من ثلاثة أحزاب لتنفيذها:
<code> var http = require ('http') ؛ var fs = require ('fs') ؛ var absubressor = require ('Abressressor') ؛ var server = http.createserver (function (req ، res) {var stream = fs.createadstream (__ dirname + '/data.txt') ؛ Stream.pipe (Abrapressor (req)). الأنابيب (الدقة) ؛}) ؛ server.listen (8000) ؛ </code>وبهذه الطريقة ، سيضغط الملف للمتصفح الذي يدعم GZIP ويفرغ. تتولى وحدة المضطهدة جميع ترميز المحتوى.
الدفق يجعل تطوير البرامج بسيطة.
3. المفاهيم الأساسية
هناك خمسة تدفقات أساسية: قابلة للقراءة ، قابلة للكتابة ، تحويل ، دوبلكس ، و "كلاسيكية".
3-1 ، الأنابيب
جميع أنواع الدفق تستخدم .pipe () لإنشاء زوج إدخال وإخراج ، وتلقي دفق SRC قابل للقراءة وإخراج بياناته إلى DST القابل للكتابة ، على النحو التالي:
<code> src.pipe (DST) </code>
تقوم طريقة .pipe (DST) بإرجاع دفق DST ، بحيث يمكن استخدام Multives .pipe () على التوالي ، على النحو التالي:
<code> A.Pipe (b) .pipe (c) .pipe (d) </code>
الوظيفة هي نفس الكود التالي:
<code> A.Pipe (B) ؛ B.Pipe (C) ؛ C.Pipe (D) ؛ </code>
3-2. تدفقات قابلة للقراءة
من خلال استدعاء طريقة .pipe () للتدفقات القابلة للقراءة ، يمكنك كتابة بيانات التدفقات القابلة للقراءة إلى دفق قابل للكتابة أو تحويل أو دوبلكس.
<code> readablestream.pipe (DST) </code>
1> إنشاء دفق قابل للقراءة
هنا ننشئ دفق قابلة للقراءة!
<code> var readable = require ('Stream'). قابلة للقراءة ؛ var rs = new appable ؛ rs.push ('beep') ؛ rs.push ('boop/n') ؛ rs.push (null) ؛ rs.pipe (process.stdout)يقوم Rs.Push (NULL) بإخطار مستلم البيانات بأن البيانات قد تم إرسالها.
لاحظ أننا لم نتصل بـ Rs.Pipe (Process.stdout) ؛ قبل دفع جميع البيانات إلى الدفق القابل للقراءة ، ولكن لا تزال جميع البيانات في الدفق القابل للقراءة الإخراج بالكامل ، لأن الدفق القابل للقراءة يخبؤ جميع البيانات المضغوطة قبل أن يقرأ المتلقي البيانات. ولكن في كثير من الحالات ، تتمثل إحدى الطرق الأفضل في الضغط فقط على البيانات في الدفق القابل للقراءة عندما تتلقى البيانات البيانات المطلوبة بدلاً من ذاكرة التخزين المؤقت للبيانات بأكملها. دعنا نعيد كتابة الدالة ._read () أدناه:
<code> var readable = require ('Stream'). var rs = readable () ؛ var c = 97 ؛ rs._read = function () {rs.push (string.fromcharcode (c ++)) ؛ if (c> 'z'.charcodeat (0)) rs.push (null) ؛ read1.jsabcdefghijklmnopqrstuvwxyz </code>ينفذ الرمز أعلاه إعادة كتابة طريقة _read () لدفع البيانات إلى الدفق القابل للقراءة فقط إذا طلب مستلم البيانات البيانات. يمكن أن تتلقى طريقة _read () أيضًا معلمة حجم تشير إلى حجم البيانات المطلوبة للبيانات ، ولكن يمكن للدفق القابل للقراءة تجاهل هذه المعلمة حسب الحاجة.
لاحظ أنه يمكننا أيضًا استخدام util.inherits () للتيارات القابلة للقراءة الملوث. لتوضيح أن طريقة _read () لا تسمى إلا عند طلب بيانات مستلم البيانات ، نقوم بتأخير عند دفع البيانات إلى الدفق القابل للقراءة ، على النحو التالي:
<code> var readable = require ('Stream'). readable ؛ var rs = readable () ؛ var c = 97 - 1 ؛ rs._read = function () {if (c> = 'z'.charcodeat (0)) respush rs.push (null) ؛ setTimeout (function () {rs.push 100) ؛} ؛ rs.pipe (process.stdout) ؛ process.on ('exit' ، function () {console.Error ('/n_read () يسمى " + (c - 97)عند تشغيل البرنامج مع الأمر التالي ، وجدنا أن طريقة _read () كانت تسمى فقط 5 مرات:
<code> $ node read2.js | HEAD -C5ABCDE_READ () يسمى 5 مرات </code>
سبب استخدام مؤقت هو أن النظام يستغرق وقتًا لإرسال إشارات لإبلاغ البرنامج بإغلاق خط الأنابيب. استخدم Process.stdout.on ("Error" ، FN) للتعامل مع النظام الذي يرسل إشارة sigpipe لأن أمر الرأس يغلق خط الأنابيب ، لأن هذا سيؤدي إلى إجراء عملية. إذا كنت ترغب في إنشاء دفق قابل للقراءة يمكن الضغط عليه في أي شكل من أشكال البيانات ، فما عليك سوى تعيين كائن المعلمة على صواب عند إنشاء الدفق ، على سبيل المثال: قابلة للقراءة ({ObjectMode: True}).
2> بيانات الدفق القابلة للقراءة
في معظم الحالات ، نستخدم ببساطة طريقة الأنابيب لإعادة توجيه بيانات الدفق القابل للقراءة إلى شكل آخر من الدفق ، ولكن في بعض الحالات قد يكون من المفيد قراءة البيانات مباشرة من الدفق القابل للقراءة. على النحو التالي:
<code> process.stdin.on ('readable' ، function () {var buf = process.stdin.read () ؛ console.dir (buf) ؛}) ؛ $ (echo abc ؛ sleep 1 ؛ echo def ؛ sleep 1 ؛ echo ghi) | الاستهلاك العقدة 0عندما تكون هناك بيانات يمكن قراءتها في الدفق القابل للقراءة ، فإن الدفق سيؤدي إلى حدوث حدث "قابل للقراءة" ، بحيث يمكن استدعاء طريقة .Read () لقراءة البيانات ذات الصلة. عندما لا تكون هناك بيانات يمكن قراءتها في الدفق القابل للقراءة ، ستعود .Rread () NULL ، بحيث يمكن إنهاء نداء .Read () وانتظار الحدث "القابل للقراءة" التالي. فيما يلي مثال على استخدام .Read (n) لقراءة 3 بايت في كل مرة من الإدخال القياسي:
<code> process.stdin.on ('readable' ، function () {var buf = process.stdin.read (3) ؛ console.dir (buf) ؛}) ؛ </code>يوضح تشغيل البرنامج على النحو التالي أن نتائج الإخراج ليست كاملة!
<code> $ (Echo ABC ؛ Sleep 1 ؛ Echo Def ؛ Sleep 1 ؛ Echo GHI) | الاستهلاك العقدة 1.js <buffer 61 = "" "62 =" "63 =" ""> <buffer 0a = "" "64 =" "65 =" ""
يجب القيام بذلك حتى يتم ترك البيانات الإضافية في المخزن المؤقت الداخلي للتيار ، ونحن بحاجة إلى إخطار الدفق الذي نريد قراءة المزيد من البيانات. قراءة (0) يمكن تحقيق ذلك.
<code> process.stdin.on ('readable' ، function () {var buf = process.stdin.read (3) ؛ console.dir (buf) ؛ process.stdin.read (0) ؛}) ؛ </code>نتائج هذا المدى كما يلي:
<code> $ (Echo ABC ؛ Sleep 1 ؛ Echo Def ؛ Sleep 1 ؛ Echo GHI) | الاستهلاك Node2.js <buffer 0a = "" 64 = "" 65 = ""> <buffer 0a = "" 68 = "" 69 = ""> </buster> </buys> </code>
يمكننا استخدام. unshift () لتغيير حجم البيانات مرة أخرى إلى رأس قائمة انتظار بيانات البث ، حتى نتمكن من الاستمرار في قراءة البيانات المذهلة. كما في الكود التالي ، سيتم إخراج محتوى الإدخال القياسي على السطر:
<code> var offset = 0 ؛ process.stdin.on ('readable' ، function () {var buf = process.stdin.read () ؛ if (! buf) return ؛ for (؛ Offset <buf.length ؛ offset ++) {if (buf [offset] === 0x0a) {console.dir buf.slice (إزاحة + 1) ؛ الإزاحة = 0 ؛ process.stdin.unshift (buf) ؛ return ؛}} process.stdin.unshift (buf) ؛}) ؛ $ tail -n +50000/usr/share/dict/dict -English | رأس -10 | خطوط العقدةبالطبع ، هناك العديد من الوحدات النمطية التي يمكنها تنفيذ هذه الوظيفة ، مثل الانقسام.
3-3. تدفقات قابلة للكتابة
لا يمكن استخدام التدفقات القابلة للكتابة إلا كمعلمة الوجهة لدالة .pipe (). الرمز التالي:
<code> src.pipe (writablestream) ؛ </code>
1> إنشاء دفق قابل للكتابة
أعد كتابة طريقة ._write (chunk ، enc ، next) لقبول البيانات من دفق قابل للقراءة.
<code> var rinsable = require ('Stream'). crinable ؛ var ws = crinable () ؛ ws._write = function (chunk ، enc ، next) {console.dir (chunk) ؛ next () ؛} ؛ process.stdin.pipe (ws) ؛ $ (echo beep 1 ؛ echo boop) | NODE write0.js <buffer 0a = "" "62 =" "65 =" "70 =" ""> <buffer 0a = "" 62 = "" 6f = "" 70 = ""> </buster> </buster> </code>قطعة المعلمة الأولى هي البيانات التي كتبها مدخل البيانات. نهاية المعلمة الثانية هي تنسيق الترميز للبيانات. المعلمة الثالثة التالية (ERR) تخطر كاتب البيانات من خلال وظيفة رد الاتصال أنه يمكن كتابة المزيد من الوقت. إذا كتب الدفق القابل للقراءة سلسلة ، فسيتم تحويل السلسلة إلى مخزن مؤقت افتراضيًا. إذا تم تعيين المعلمة القابلة للكتابة ({decodestrings: false}) عند إنشاء الدفق ، فلن يتم إجراء التحويل. إذا تمت كتابة البيانات بواسطة الدفق القابل للقراءة ، فأنت بحاجة إلى إنشاء دفق قابل للكتابة بهذه الطريقة
<code> قابل للكتابة ({objectmode: true}) </code>2> اكتب البيانات إلى دفق قابل للكتابة
اتصل بالطريقة .write (البيانات) للدفق القابل للكتابة لإكمال كتابة البيانات.
<code> process.stdout.write ('beep boop/n') ؛ </code>يدعو الاتصال.
<code> var fs = require ('fs') ؛ var ws = fs.createwRiteStream ('message.txt') ؛ ws.write ('beep') ؛ setTimeout (function () {ws.end ('boop/n') ؛} ، 1000)إذا كنت بحاجة إلى ضبط حجم المخزن المؤقت للتيار القابل للكتابة ، فعند إنشاء الدفق ، فأنت بحاجة إلى تعيين Opts.Highwatermark ، بحيث إذا تجاوزت البيانات في المخزن المؤقت Opts.highwatermark ، ستعود طريقة. write (البيانات). عندما يكون المخزن المؤقت قابلاً للكتابة ، فإن الدفق القابل للكتابة سيؤدي إلى حدوث حدث "استنزاف".
3-4. تيارات كلاسيكية
Classic Streams هي واجهة أقدم ، ظهرت لأول مرة في إصدار Node 0.4 ، ولكن لا يزال من الجيد جدًا فهم مبدأ التشغيل.
أين. عندما يتم تسجيل دفق في حدث "البيانات" مرة أخرى إلى الوظيفة ، سيعمل الدفق في وضع الإصدار القديم ، أي أن واجهة برمجة التطبيقات القديمة سيتم استخدامها.
1> تيارات قابلة للقراءة الكلاسيكية
حدث Classic Readable Streams هو مشغل حدث. إذا كان لدى كلاسيك تيارات قابلة للقراءة بيانات للقراءة ، فإنها تؤدي إلى حدث "البيانات". عند قراءة البيانات ، سيتم تشغيل حدث "النهاية". تحدد طريقة .pipe () ما إذا كان الدفق يحتوي على بيانات لقراءة عن طريق التحقق من قيمة Stream.Readable. فيما يلي مثال على طباعة حروف AJ باستخدام التدفقات الكلاسيكية القابلة للقراءة:
<code> var tream = require ('dream') ؛ var dream = new dream ؛ dream.Readable = true ؛ var c = 64 ؛ var iv = setInterval (function () {if (++ c> = 75) {clearinterval (iv) ؛ stream.emit ('end') ؛ 100)إذا كنت ترغب في قراءة البيانات من الدفق الكلاسيكي القابل للقراءة ، فقم بتسجيل وظائف رد الاتصال لأحداث "البيانات" و "النهاية" ، والرمز كما يلي:
<code> process.stdin.on ('data' ، function (buf) {console.log (buf) ؛}) ؛ process.stdin.on ('end' ، function () {console.log ('__ end __') ؛}) ؛ $ (echo beep ؛ sleep 1 ؛ echo boop) | Node Classic1.js <Buffer 0a = "" "62 =" "65 =" "70 =" ""> <Buffer 0a = "" 62 = "" 6f = "" 70 = ""> __ End __ </Buffer> </buster> </code>تجدر الإشارة إلى أنه إذا كنت تستخدم هذه الطريقة لقراءة البيانات ، فسوف تفقد فوائد استخدام الواجهة الجديدة. على سبيل المثال ، عندما تكتب بيانات إلى دفق مع زمن انتقال مرتفع للغاية ، تحتاج إلى الانتباه إلى التوازن بين قراءة البيانات والكتابة ، وإلا فإن ذلك سيؤدي إلى تخزين كمية كبيرة من البيانات في الذاكرة ، مما يؤدي إلى مضيعة للكثير من الذاكرة. بشكل عام ، يوصى بشدة باستخدام طريقة .pipe () للتيار ، بحيث لا تضطر إلى الاستماع إلى أحداث "البيانات" و "النهاية" بنفسك ، ولا داعي للقلق بشأن مشكلة القراءة والكتابة غير المتوازنة. بالطبع ، يمكنك أيضًا استخدام من خلال الاستماع إلى أحداث "البيانات" و "النهاية" بنفسك ، مثل الرمز التالي:
<code> var through = require ('to') ؛ process.stdin.pipe (من خلال (الكتابة ، النهاية)) العقدة من خلالأو يمكنك أيضًا استخدام Concat-Stream لتخزين محتوى الدفق بأكمله:
<code> var concat = require ('concat-stream') ؛ process.stdin.pipe (concat (function (body) {console.log (json.parse (body)) ؛}) ؛ $ echo '{"beep": "boop"}' | عقدة concat.js {beep: 'boop'} </code>بالطبع ، إذا كان عليك الاستماع إلى أحداث "البيانات" و "النهاية" بنفسك ، فيمكنك استخدام طريقة .pause () لإيقاف التدفقات القابلة للقراءة الكلاسيكية والاستمرار في تشغيل حدث "البيانات" عندما لا يكون دفق كتابة بيانات الكتابة قابلاً للكتابة. انتظر حتى يتم كتابة بيانات كتابة الدفق قبل استخدام طريقة .resume () لإعلام الدفق لمواصلة تشغيل حدث "البيانات" لمواصلة القراءة.
بيانات.
2> تيارات كلاسيكية قابلة للكتابة
الجداول الكلاسيكية القابلة للكتابة بسيطة للغاية. لا يوجد سوى ثلاث طرق: .write (buf) و .end (buf) و .destroy (). معلمة BUF لطريقة .end (BUF) اختيارية. إذا قمت بتحديد هذه المعلمة ، فهذا يعادل Dream.write (buf) ؛ Stream.end (). تجدر الإشارة إلى أنه عندما يكون المخزن المؤقت للتيار ممتلئًا ، فلا يمكن كتابة الدفق. ستعود طريقة الكتابة (BUF) FALSE. إذا كان الدفق قابل للكتابة مرة أخرى ، فإن الدفق سيؤدي إلى حدوث حدث التصريف.
4. التحويل
التحويل هو دفق يقوم بتصفية إخراج بيانات القراءة.
5
Duplex Stream هو دفق ثنائي الاتجاه يمكن قراءته أو مكتوته. على سبيل المثال ، أدناه هو دفق دوبلكس:
<code> A.Pipe (b) .pipe (a) </code>
المحتوى أعلاه هو دليل مستخدم تدفق بيانات NodeJS الذي قدمه لك المحرر. آمل أن يكون ذلك مفيدًا لك!