Menu

fs/promises ב-Node.js: מערכת הקבצים עם async/await

fs/promises היא גרסת ה-promises של המודול fs ב-Node: ‏readFile, writeFile, readdir, stat, mkdir, rm ו-rename מחזירות כולן promises שעושים להם await, ושגיאות נתפסות עם try/catch.

בדף הזה יש עורכים שאפשר להריץ. לערוך, להריץ ולראות את הפלט מיד.

fs/promises היא הגרסה של המודול fs של Node שמבוססת על promises. הפונקציות שלה (readFile, writeFile, readdir, stat, mkdir, rm, rename וכל השאר) מחזירות promise במקום לקבל callback, כך שעושים להן await ותופסים שגיאות עם try/catch. ייבאו אותה עם require('fs/promises') או import fs from 'fs/promises'.

הוא מדפיס [ 'write docs', 'ship it' ]: כל await מחכה לפעולה אחת לפני שהשורה הבאה רצה, כך שההוספה תמיד קורית אחרי הכתיבה הראשונה. ב-ES module, ‏await עובד ברמה העליונה; ב-CommonJS, עטפו את הקוד בפונקציה async.

הפונקציות שתשתמשו בהן הכי הרבה

השמות זהים ל-API של ה-callbacks, רק בלי ה-callback. הדפים על readFile ועל writeFile מכסים את שתיהן בפירוט.

פונקציהמה היא עושהמה ה-promise מחזיר
readFile(path, 'utf8')קוראת קובץ שלםאת התוכן
writeFile(path, data)יוצרת או מחליפה קובץundefined
appendFile(path, data)מוסיפה לסוףundefined
readdir(path)מציגה את תוכן התיקייהמערך של שמות
stat(path)גודל, תאריכים, קובץ או תיקייהאובייקט Stats
mkdir(path, { recursive: true })יוצרת תיקיותאת התיקייה הראשונה שנוצרה, או undefined
rename(from, to)מעבירה או משנה שםundefined
rm(path, { recursive: true, force: true })מוחקת קובץ או תיקייהundefined

רשימת תיקייה עם readdir ו-stat

readdir מחזיר שמות בלבד. קראו ל-stat על כל אחד כדי לקבל את הגודל שלו או להבדיל בין קבצים לתיקיות, או העבירו { withFileTypes: true } כדי לקבל רשומות שכבר יודעות מה הסוג שלהן.

הלולאה מדפיסה README.md 6 bytes, notes.txt 3 bytes ו-src folder. stat נדחה עם ENOENT עבור נתיב שלא קיים, וזה הופך אותו לבדיקת קיום מהירה כשאתם רוצים גם את הפרטים.

שינוי שם, העברה ומחיקה

rename גם משנה שם וגם מעביר קובץ (באותו כונן). rm מוחק; הוא צריך recursive: true בשביל תיקייה שיש בה תוכן, ו-force: true כדי לא להיכשל כשהנתיב כבר לא קיים.

הקובץ עובר מ-drafts ל-published בשמו החדש, ואחרי rm התיקייה blog מכילה רק את published. הסירו את force: true מה-rm השני והוא יידחה עם ENOENT.

טיפול בשגיאות עם try/catch

פעולה שנכשלת דוחה את ה-promise שלה. עם await, הדחייה הזו הופכת לחריגה שתופסים כמו כל חריגה אחרת. ה-code של השגיאה אומר מה השתבש.

הקריאה הראשונה לא מוצאת קובץ ומחזירה []; השנייה מדפיסה Ada. קודים שכדאי להכיר: ENOENT (אין קובץ כזה), EEXIST (כבר קיים), EISDIR (תיקייה במקום שבו ציפו לקובץ), ENOTEMPTY (בתיקייה עדיין יש תוכן) ו-EACCES (אין הרשאה).

promise שלא עושים לו await ולא .catch() הופך ל-unhandled rejection כשהוא נכשל, ו-Node יוצא עם שגיאה. עשו await לכל קריאה, או החזירו את ה-promise שלה לקוד קורא שעושה זאת.

קריאה של כמה קבצים בבת אחת עם Promise.all

לולאת for עם await קוראת קבצים אחד אחרי השני. כשהקריאות לא תלויות זו בזו, התחילו את כולן וחכו פעם אחת עם Promise.all: הדיסק עובד עליהן באותו זמן.

Promise.all שומר על הסדר של מערך הקלט, כך שהתוצאה היא [ 'alpha', 'beta', 'gamma' ] לא משנה איזו קריאה הסתיימה ראשונה, והאורך המחובר הוא 14. שנו את 'b.txt' במערך ל-'x.txt', וכל ה-await יידחה עם ENOENT.

אם אתם צריכים כל תוצאה גם כשחלק מהקריאות נכשלות, השתמשו ב-Promise.allSettled; הדף על promises מכסה את שתיהן.

טעויות נפוצות

לבלבל בין שני המודולים. require('fs').readFile רוצה callback; require('fs/promises').readFile מחזיר promise. קריאה לגרסת ה-callback בלי callback זורקת The "cb" argument must be of type function.

לשכוח await. const text = fs.readFile(...) הוא promise שעוד ממתין, ו-text.split זורק text.split is not a function.

כתיבות במקביל לקובץ אחד. Promise.all עם כמה כתיבות לאותו נתיב יכול להשאיר תוכן מעורבב. הריצו אותן אחת אחרי השנייה.

שאלות נפוצות

מה ההבדל בין fs.promises ל-fs/promises?

אין הבדל. require('fs').promises ו-require('fs/promises') הם אותו אובייקט. הנתיב fs/promises (או node:fs/promises) הוא הדרך המקובלת לייבא אותו היום.

יש existsSync ב-fs/promises?

לא. ב-API של ה-promises יש access, שנדחה כשהנתיב חסר, אבל הדפוס המקובל הוא לקרוא לפעולה שאתם צריכים ולתפוס ENOENT. existsSync מהמודול fs הרגיל עדיין עובד לבדיקות מהירות.

האם fs/promises איטי יותר מה-API של ה-callbacks?

מעט, בגלל התקורה של ה-promises, אבל זה זניח לעומת זמן הדיסק. בחרו לפי קריאוּת; ה-API של ה-promises הוא זה שרוב הקוד החדש משתמש בו.

איך הופכים פונקציית fs עם callback ל-promise?

כמעט אף פעם לא צריך, כי fs/promises מכסה את פונקציות הקבצים. ל-APIs אחרים עם callbacks, util.promisify(fn) מחזיר גרסה שמחזירה promise.

איור של שפות התכנות ב-Coddy

ללמוד תכנות עם Coddy

להתחיל