成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

精讀《如何利用 Nodejs 監(jiān)聽文件夾》

李濤 / 3316人閱讀

摘要:引言本期精讀的文章是,探討如何監(jiān)聽文件的變化。概述使用使用內(nèi)置函數(shù)似乎可以解決問題但你可能會發(fā)現(xiàn)這個回調(diào)執(zhí)行有一定延遲,因為是通過輪詢檢測文件變化的,它并不能實時作出反饋,而且只能監(jiān)聽一個文件,存在效率問題。

1 引言

本期精讀的文章是:How to Watch for Files Changes in Node.js,探討如何監(jiān)聽文件的變化。

如果想使用現(xiàn)成的庫,推薦 chokidar 或 node-watch,如果想了解實現(xiàn)原理,請往下閱讀。

2 概述 使用 fs.watchfile

使用 fs 內(nèi)置函數(shù) watchfile 似乎可以解決問題:

fs.watchFile(dir, (curr, prev) => {});

但你可能會發(fā)現(xiàn)這個回調(diào)執(zhí)行有一定延遲,因為 watchfile 是通過輪詢檢測文件變化的,它并不能實時作出反饋,而且只能監(jiān)聽一個文件,存在效率問題。

使用 fs.watch

使用 fs 的另一個內(nèi)置函數(shù) watch 是更好的選擇:

fs.watch(dir, (event, filename) => {});

watch 通過操作系統(tǒng)提供的文件更改通知機制,在 Linux 操作系統(tǒng)使用 inotify,在 macOS 系統(tǒng)使用 FSEvents,在 windows 系統(tǒng)使用 ReadDirectoryChangesW,而且可以用來監(jiān)聽目錄的變化,在監(jiān)聽文件夾的場景中,比創(chuàng)建 N 個 fs.watchfile 效率高出很多。

$ node file-watcher.js
[2018-05-21T00:55:52.588Z] Watching for file changes on ./button-presses.log
[2018-05-21T00:56:00.773Z] button-presses.log file Changed
[2018-05-21T00:56:00.793Z] button-presses.log file Changed
[2018-05-21T00:56:00.802Z] button-presses.log file Changed
[2018-05-21T00:56:00.813Z] button-presses.log file Changed

但當我們修改一個文件時,回調(diào)卻執(zhí)行了 4 次!原因是文件被寫入時,可能觸發(fā)多次寫操作,即使只保存了一次。但我們不需要這么敏感的回調(diào),因為通常認為一次保存就是一次修改,系統(tǒng)底層寫了幾次文件我們并不關心。

因而可以進一步判斷是否觸發(fā)狀態(tài)是 change:

fs.watch(dir, (event, filename) => {
  if (filename && event === "change") {
    console.log(`${filename} file Changed`);
  }
});

這樣做可以一定程度解決問題,但作者發(fā)現(xiàn) Raspbian 系統(tǒng)不支持 rename 事件,如果歸類為 change,會導致這樣的判斷毫無意義。

作者要表達的意思是,在不同平臺下,fs.watch 的規(guī)則可能會不同,原因是 fs.watch 分別使用了各平臺提供的 api,所以無法保證這些 api 實現(xiàn)規(guī)則的統(tǒng)一性。
優(yōu)化方案一:對比文件修改時間

基于 fs.watch,增加了對修改時間的判斷:

let previousMTime = new Date(0);

fs.watch(dir, (event, filename) => {
  if (filename) {
    const stats = fs.statSync(filename);
    if (stats.mtime.valueOf() === previousMTime.valueOf()) {
      return;
    }
    previousMTime = stats.mtime;
    console.log(`${filename} file Changed`);
  }
});

log 由 4 個變成了 3 個,但依然存在問題。我們認為文件內(nèi)容變化才算有修改,但操作系統(tǒng)考慮的因素更多,所以我們再嘗試對比文件內(nèi)容是否變化。

筆者補充:另外一些開源編輯器可能先清空文件再寫入,也會影響到觸發(fā)回調(diào)的次數(shù)。
優(yōu)化方案二:校驗文件 md5

只有文件內(nèi)容變化了,才認為觸發(fā)了改動,這下總可以了吧:

let md5Previous = null;

fs.watch(dir, (event, filename) => {
  if (filename) {
    const md5Current = md5(fs.readFileSync(buttonPressesLogFile));
    if (md5Current === md5Previous) {
      return;
    }
    md5Previous = md5Current;
    console.log(`${filename} file Changed`);
  }
});

log 終于由 3 個變成了 2 個,為什么多出一個?可能的原因是,在文件保存過程中,系統(tǒng)可能會觸發(fā)多個回調(diào)事件,也許存在中間態(tài)。

優(yōu)化方案三:加入延遲機制

我們嘗試延遲 100 毫秒進行判斷,也許能避開中間狀態(tài):

let fsWait = false;
fs.watch(dir, (event, filename) => {
  if (filename) {
    if (fsWait) return;
    fsWait = setTimeout(() => {
      fsWait = false;
    }, 100);
    console.log(`${filename} file Changed`);
  }
});

這下 log 變成一個了。很多 npm 包在這里使用了 debounce 函數(shù)控制觸發(fā)頻率,才將觸發(fā)頻率修正。

而且我們需要結(jié)合 md5 與延遲機制共同作用,才能得到相對精準的結(jié)果:

let md5Previous = null;
let fsWait = false;
fs.watch(dir, (event, filename) => {
  if (filename) {
    if (fsWait) return;
    fsWait = setTimeout(() => {
      fsWait = false;
    }, 100);
    const md5Current = md5(fs.readFileSync(dir));
    if (md5Current === md5Previous) {
      return;
    }
    md5Previous = md5Current;
    console.log(`${filename} file Changed`);
  }
});
3 精讀

作者討論了一些實現(xiàn)文件夾監(jiān)聽的基本方式,可以看出,使用了各平臺原生 API 的 fs.watch 并不那么靠譜,但這也我們監(jiān)聽文件的唯一手段,所以需要基于它進行一系列優(yōu)化。

而實際場景中,還需要考慮區(qū)分文件夾與文件、軟連接、讀寫權(quán)限等情況。

另外用在生產(chǎn)環(huán)境的庫,也基本使用 50 到 100 毫秒解決重復觸發(fā)的問題。

所以無論 chokidar 或 node-watch,都大量使用了文中提及的技巧,再加上對邊界條件的處理,對軟連接、權(quán)限等情況處理,將所有可能情況都考慮到,才能提供較為準確的回調(diào)。

比如判斷文件寫入操作是否完畢,也需要通過輪詢的方式:

function awaitWriteFinish() {
  // ...省略
  fs.stat(
    fullPath,
    function(err, curStat) {
      // ...省略

      if (prevStat && curStat.size != prevStat.size) {
        this._pendingWrites[path].lastChange = now;
      }

      if (now - this._pendingWrites[path].lastChange >= threshold) {
        delete this._pendingWrites[path];
        awfEmit(null, curStat);
      } else {
        timeoutHandler = setTimeout(
          awaitWriteFinish.bind(this, curStat),
          this.options.awaitWriteFinish.pollInterval
        );
      }
    }.bind(this)
  );
  // ...省略
}

可以看出,第三方 npm 庫都采取不信任操作系統(tǒng)回調(diào)的方式,根據(jù)文件信息完全重寫了判斷邏輯。

可見,信任操作系統(tǒng)的回調(diào),就無法抹平所有操作系統(tǒng)間的差異,唯有統(tǒng)一重寫文件的 “寫入”、“刪除”、“修改” 等邏輯,才能保證在全平臺的兼容性。

4 總結(jié)

利用 nodejs 監(jiān)聽文件夾變化很容易,但提供準確的回調(diào)卻很難,主要難在兩點:

抹平操作系統(tǒng)間的差異,這需要在結(jié)合 fs.watch 的同時,增加一些額外校驗機制與延時機制。

分清楚操作系統(tǒng)預期與用戶預期,比如編輯器的額外操作、操作系統(tǒng)的多次讀寫都應該被忽略,用戶的預期不會那么頻繁,會忽略極小時間段內(nèi)的連續(xù)觸發(fā)。

另外還有兼容性、權(quán)限、軟連接等其他因素要考慮,fs.watch 并不是一個開箱可用的工程級別 api。

5 更多討論
討論地址是:精讀《如何利用 Nodejs 監(jiān)聽文件夾》 · Issue #87 · dt-fe/weekly

如果你想?yún)⑴c討論,請點擊這里,每周都有新的主題,周末或周一發(fā)布。

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/107952.html

相關文章

  • 精讀如何nodejs 使用環(huán)境變量》

    摘要:引言本期精讀的文章是如何在使用環(huán)境變量。介紹了開發(fā)與生產(chǎn)環(huán)境如何管理環(huán)境變量。本地通過調(diào)試環(huán)境變量既方便又安全。更多討論討論地址是精讀如何在使用環(huán)境變量如果你想?yún)⑴c討論,請點擊這里,每周都有新的主題,周末或周一發(fā)布。 1 引言 本期精讀的文章是:如何在 nodejs 使用環(huán)境變量。 介紹了開發(fā)與生產(chǎn)環(huán)境如何管理環(huán)境變量。 這里環(huán)境變量指的是數(shù)據(jù)庫密碼等重要數(shù)據(jù),而不是指普通變量傳參。 ...

    aaron 評論0 收藏0
  • 精讀《React PowerPlug 源碼》

    摘要:今天我們就來解讀一下的源碼。比較有意思,將定時器以方式提供出來,并且提供了方法。實現(xiàn)方式是,在組件內(nèi)部維護一個定時器,實現(xiàn)了組件更新銷毀時的計時器更新銷毀操作,可以認為這種定時器的生命周期綁定了組件的生命周期,不用擔心銷毀和更新的問題。 1. 引言 React PowerPlug 是利用 render props 進行更好狀態(tài)管理的工具庫。 React 項目中,一般一個文件就是一個類,...

    teren 評論0 收藏0
  • 精讀《怎么用 React Hooks 造輪子》

    摘要:可以看到,這樣不僅沒有占用組件自己的,也不需要手寫回調(diào)函數(shù)進行處理,這些處理都壓縮成了一行。效果通過拿到周期才執(zhí)行的回調(diào)函數(shù)。實現(xiàn)等價于的回調(diào)僅執(zhí)行一次時,因此直接把回調(diào)函數(shù)拋出來即可。 1 引言 上周的 精讀《React Hooks》 已經(jīng)實現(xiàn)了對 React Hooks 的基本認知,也許你也看了 React Hooks 基本實現(xiàn)剖析(就是數(shù)組),但理解實現(xiàn)原理就可以用好了嗎?學的是...

    Shihira 評論0 收藏0
  • 精讀《Serverless 給前端帶來了什么》

    摘要:前端框架總是帶入后端思維,而則是把前端思維帶入了后端運維。前端同學對應該尤為激動。而帶來了進一步優(yōu)化的空間。當服務器面臨攻擊重啟磁盤故障時,打開復雜的工作臺或登陸后一通操作才能恢復。 1. 引言 Serverless 是一種 無服務器架構(gòu),讓用戶無需關心程序運行環(huán)境、資源及數(shù)量,只要將精力 Focus 到業(yè)務邏輯上的技術。 現(xiàn)在公司已經(jīng)實現(xiàn) DevOps 化,正在向 Serverles...

    wizChen 評論0 收藏0
  • 精讀Nodejs V12》

    摘要:更好的安全性隨著的發(fā)布,從升級到了,更安全且更易配置。通過使用,程序可以減少握手所需時間來提升請求性能。提供診斷報告有一項實驗功能,根據(jù)用戶需求提供診斷報告,包括崩潰性能下降內(nèi)存泄露使用高等等。前端精讀幫你篩選靠譜的內(nèi)容。 1. 引言 Node12 發(fā)布有幾個月了,讓我們跟隨 Nodejs 12 一起看看 Node12 帶來了哪些改變。 2. 概述 Node12 與以往的版本不同,帶來...

    CoderStudy 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<