2016-04-01 7 views
1

Ich versuche, Verzeichnisbaum mit RxJs und node.js zu durchlaufen.Verzeichnisbaum in node.js mit RxJs durchsuchen

kam ich mit Arbeits Lösung:

const filesInDir = Rx.Observable.fromNodeCallback(fs.readdir) 
const statFile = Rx.Observable.fromNodeCallback(fs.stat) 

const listFiles = (prefix, dir = '') => { 
    const file$ = filesInDir(`${prefix}/${dir}`) 
     .flatMap(file => file) 
     .filter(file => !file.startsWith('.')) 
    const isDir$ = file$ 
     .map(file => statFile(`${prefix}/${dir}/${file}`)) 
     .flatMap(file => file) 
     .map(file => file.isDirectory()) 
    return file$ 
     .zip(isDir$, (file, isDir) => {return {file, isDir}}) 
     .map(f => { 
      if (f.isDir) { 
       return listFiles(prefix, `${dir}/${f.file}`) 
      } 
      return Rx.Observable.return(`${dir}/${f.file}`) 
     }) 
     .flatMap(file => file) 
} 

listFiles('public') 
    .toArray() 
    .subscribe(list => { 
     console.log(list) 
    }) 

Fragen:

  1. Gibt es effiziente/prägnante Art und Weise zu .map asynchronem Betrieb mit?
  2. Gleiche Frage für die .zip Teil

Antwort

2

Große Frage.

Ich denke, Sie können ein paar Dinge tun, um diese Abfrage zu optimieren.

Zuerst können wir die map Opertors gefolgt von .flatMap(file => file) zu nur einer einzigen FlatMap ändern. Kleine Verbesserung, aber wird weniger Code ausführen.

const file$ = filesInDir(`${prefix}/${dir}`) 
    .flatMap(file => file) 
    .filter(file => !file.startsWith('.')) 
const isDir$ = file$ 
    .flatMap(file => statFile(`${prefix}/${dir}/${file}`)) 
    .map(file => file.isDirectory()) 
return file$ 
    .zip(isDir$, (file, isDir) => {return {file, isDir}}) 
    .flatMap(f => { 
     if (f.isDir) { 
      return listFiles(prefix, `${dir}/${f.file}`) 
     } 
     return Rx.Observable.return(`${dir}/${f.file}`) 
    }) 

Die Hauptverbesserung ist, wo ich glaube, dass Sie tatsächlich das Dateisystem zweimal schlagen. Die beobachtbare Sequenz filesInDir ist keine Hot/Cached-Sequenz. Wenn ja, würde das rekursive Gehen des Verzeichnisbaums nicht funktionieren. In diesem Sinne rufen Sie es einmal, um alle Dateien zu erhalten, und dann rufen Sie es erneut, um die isDirectory Prüfung zu tun. Dies führt sowohl zu einem möglichen Leistungsaufwand als auch zu einem Fehler. Sie gehen davon aus, dass die Reihenfolge der zurückgegebenen Dateien immer der gleichen Reihenfolge entspricht, wenn Sie auf die Festplatte klicken. Auch wenn wir für eine Sekunde ignorieren, ist diese Festplatte veränderbar und könnte unter Ihnen ändern. Sie können in einer asynchronen Welt garantieren, dass die Sequenzen in der gleichen Reihenfolge zurückgegeben werden. Auf meinem Computer (Windows 10) wird die Reihenfolge meistens in der gleichen Reihenfolge zurückgegeben. Jedoch mit einem tief genug Baum (z. B. von _C: _) Ich traf jedes Mal eine Diskrepanz.

Wie auch immer, die Performance-Korrektur ist auch der Bugfix. Anstatt jedes Mal erneut aus dem Dateisystem zu lesen, können wir es einmal tun. Verschieben den statFile() Anruf in ein flatMap, die auch das Ergebnis Karten mit der Schließung der zu statFile

const listFiles = (prefix, dir) => { 
    return file$ = filesInDir(`${prefix}/${dir}`) 
     .flatMap(file => file) 
     .filter(file => !file.startsWith('.')) 
     .flatMap(file => statFile(`${prefix}/${dir}/${file}`) 
        .map(sf => {return {file, isDir: sf.isDirectory()}})) 
     .flatMap(f => { 
      if (f.isDir) { 
       return listFiles(prefix, `${dir}/${f.file}`) 
      } 
      return Rx.Observable.return(`${dir}/${f.file}`) 
     }) 
} 

bestand Datei versucht Dies hat auch den Vorteil, die Zip Klausel zu entfernen, weil wir nicht mehr mit zwei Sequenzen arbeiten .