חידון: שליטה ב-IO של NodeJS
בחן את הידע שלך בקבצים, זרמים ומאגרים
מוכן לצלול לעולם ה‑IO של NodeJS? 🌊
החידון הזה יבחן את ההבנה שלך בפעולות ה‑IO של Node, החל מפעולות מערכת קבצים בסיסיות ועד למושגי סטרימינג מתקדמים. נסקור buffers, encoding ושיטות עבודה מומלצות לטיפול יעיל בנתונים.
בוא נראה עד כמה אתה מכיר את ה‑streams שלך לעומת ה‑buffers שלך! 🚀
מה הקוד הזה עושה?
const buf = Buffer.alloc(5);console.log(buf);Buffer.alloc(size) יוצר Buffer חדש בגודל שצוין וממולא באפסים.
הפלט יהיה: <Buffer 00 00 00 00 00>
אם ברצונך ליצור Buffer עם נתונים אקראיים, השתמש ב-Buffer.allocUnsafe(5).
מה יודפס?
const buf = Buffer.from([65]);console.log(buf.toString());המספרים במערך מייצגים קודי ASCII:
- 65: ‘A’
toString() ממיר את הבתים האלה לייצוג מחרוזת באמצעות קידוד UTF-8 כברירת מחדל.
מה סדר הפלט?
import fs from 'fs';fs.readFile('test.txt', 'utf8', (err, data) => { console.log(data);});console.log('Done');מכיוון ש-readFile הוא אסינכרוני, הקוד ממשיך להתבצע בזמן שהקובץ נקרא.
לכן, “Done” יודפס לפני תוכן הקובץ.
כדי לחכות שהקובץ ייקרא קודם, תוכל להשתמש בגרסה מבוססת Promise:
import { promises as fs } from 'fs';
async function read() { const data = await fs.readFile('test.txt', 'utf8'); console.log(data); console.log('Done');}מה fs.readFileSync() מחזיר כברירת מחדל?
import fs from 'fs';const content = fs.readFileSync('test.txt');fs.readFileSync() מחזיר Buffer כברירת מחדל כאשר לא צוין קידוד. אם אתה רוצה מחרוזת, עליך לבצע אחת מהאפשרויות הבאות:
- ציין קידוד:
fs.readFileSync('test.txt', 'utf8') - המר את ה-Buffer:
content.toString()
איזו קבוצת אירועים משמשים בדרך כלל עם זרמי Readable?
זרמי Readable פולטים מספר אירועים חשובים:
- ‘data’: כאשר נתונים זמינים לקריאה
- ‘end’: כאשר אין עוד נתונים לקריאה
- ‘error’: כאשר מתרחשת שגיאה
- ‘close’: כאשר הזרם והמשאב הבסיסי נסגרו
const readable = fs.createReadStream('file.txt');readable.on('data', chunk => console.log(chunk));readable.on('end', () => console.log('Done!'));מה הקוד הזה עושה?
import fs from 'fs';const readable = fs.createReadStream('source.txt');const writable = fs.createWriteStream('dest.txt');readable.pipe(writable);pipe() מחבר זרם קריא לזרם בר-כתיבה, תוך ניהול אוטומטי של לחץ אחורי (backpressure) והעתקת נתונים בנתחים מבלי לטעון את כל הקובץ לזיכרון.
זה חסכוני בזיכרון עבור קבצים גדולים בהשוואה ל-fs.readFile() ואחריו fs.writeFile().
מה עושה האפשרות recursive?
import fs from 'fs';fs.mkdirSync('./a/b/c', { recursive: true });האפשרות recursive: true יוצרת תיקיות אב אם הן לא קיימות.
ללא אפשרות זו, ניסיון ליצור ’./a/b/c’ יזרוק שגיאה אם ’./a’ או ’./a/b’ לא קיימים.
זה דומה לפקודת shell mkdir -p.
מה ייצא?
import { Transform } from 'stream';const upperCase = new Transform({ transform(chunk, encoding, callback) { callback(null, chunk.toString().toUpperCase()); }});process.stdin .pipe(upperCase) .pipe(process.stdout);// Input: "hello world"זרמי טרנספורמציה משנים נתונים כשהם עוברים דרכם. כאן, כל נתח:
- מומר למחרוזת
- מומר לאותיות גדולות
- מועבר ל-stdout
זה יוצר צינור שממיר את כל הקלט לאותיות גדולות.
כמה פעמים מובטח ש-fs.watch() יופעל כאשר קובץ משתנה?
import fs from 'fs';fs.watch('test.txt', (eventType, filename) => { console.log(`${filename} was changed`);});// Then modify test.txt oncefs.watch() אינו מובטח שיופעל בדיוק פעם אחת לכל שינוי לוגי בקובץ. לעתים קרובות הוא מופעל מספר פעמים מכיוון שעורכי טקסט רבים:
- שומרים לקובץ זמני
- משנים את שמו לקובץ היעד
לצפייה אמינה יותר, שקול להשתמש ב:
- החבילה
chokidar - דיבוב (debouncing) של הקולבק
- שימוש ב-
fs.watchFile()(אם כי זה פחות יעיל)
מה הפלט?
const buf1 = Buffer.from('Hello');const buf2 = Buffer.from('Hello');console.log(buf1 === buf2);Buffers מושווים לפי הפניה, לא לפי ערך. למרות שהם מכילים את אותם נתונים, הם אובייקטים שונים.
כדי להשוות תוכן של Buffer, השתמש ב:
buf1.equals(buf2) // true// orBuffer.compare(buf1, buf2) === 0 // trueמה המטרה העיקרית של Backpressure בזרמים?
Backpressure הוא מנגנון שמונע גלישת זיכרון על ידי השהיית הקריאה כאשר צד הכתיבה לא יכול לעמוד בקצב.
דוגמה ל-Backpressure ידני:
readable.on('data', (chunk) => { const canContinue = writable.write(chunk); if (!canContinue) { readable.pause(); writable.once('drain', () => readable.resume()); }});pipe() מטפל בזה באופן אוטומטי!
מה הקוד הזה עושה?
import fs from 'fs';fs.symlinkSync('target.txt', 'link.txt');symlinkSync יוצר קישור סימבולי (כמו קיצור דרך) לקובץ היעד.
הבדלים עיקריים מקישורים קשיחים:
- יכול לקשר לתיקיות
- יכול לחצות מערכות קבצים
- נשבר אם היעד נמחק
כדי ליצור קישור קשיח במקום:
fs.linkSync('target.txt', 'hardlink.txt');באילו מצבים יכולים לפעול סטרימים ב-Node.js?
סטרימים יכולים לפעול ב:
- מצב בינארי (ברירת מחדל): עבור buffers ומחרוזות
- מצב אובייקט: עבור כל ערך JavaScript
דוגמה למצב אובייקט:
import { Transform } from 'stream';const objectStream = new Transform({ objectMode: true, transform(chunk, encoding, callback) { callback(null, { value: chunk }); }});איזה טיפוס הוא הפרמטר fd בקולבק הזה?
import fs from 'fs';fs.open('test.txt', 'r', (err, fd) => { console.log(typeof fd);});מתארי קבצים הם מספרים המזהים באופן ייחודי קבצים פתוחים במערכת ההפעלה.
שלושת מתארי הקבצים הראשונים שמורים:
- 0: stdin
- 1: stdout
- 2: stderr
תמיד זכור לסגור מתארי קבצים:
fs.close(fd, (err) => { if (err) throw err;});כמה בתים יתפוס המחרוזת הזו ב-UTF-8?
const str = "Hello 🌍";const buf = Buffer.from(str);console.log(buf.length);ב-UTF-8:
- תווים ASCII (כמו ‘Hello ’) תופסים בית אחד כל אחד
- האימוג’י של כדור הארץ 🌍 תופס 4 בתים
אז: 5 (Hello) + 1 (רווח) + 4 (🌍) = 10 בתים
כדי לראות את הבתים:
console.log(buf); // <Buffer 48 65 6c 6c 6f 20 f0 9f 8c 8d>אני מקווה שנהניתם לבחון את הידע שלכם ב-NodeJS IO! רוצים עוד? עיינו באוסף החידונים שלי לעוד אתגרים!