2017-06-19 3 views
0

Ich bin neu im Knoten und ich habe ein Problem mit der Auflösung eines asynchronen Versprechens. Mein Versprechen löst nicht auf und ich bin mir nicht sicher, was ich falsch gemacht habe. Ich habe immer noch Probleme, Versprechungen und Rückrufe zu verstehen, daher ist jedes Feedback hilfreich.Das Versprechen löst nicht

var filterFiles = function(){ 
    return new Promise(function(resolve, reject){ 
     fs.readdir(rootDir, function(err, files){ 
     if(err) return console.log(err); 

     var task = function(file){ 
      return new Promise(function(resolve, reject){ 
      if(! /^\..*/.test(file)){ 
       fs.stat(rootDir + '/' + file, function(err, stats){ 
       if(stats.isDirectory()){ 
        dirArray.push(file); 
        console.log(dirArray.length); 
        resolve(file); 
       } 
       if(stats.isFile()){ 
        fileArray.push(file); 
        console.log(fileArray.length); 
        resolve(file); 
       } 
       }) 
      } 

      }) 
     }; 

     var actions = files.map(task); 

     return Promise.all(actions).then(function(resolve, reject){ 
      resolve({dirArray: dirArray, fileArray: fileArray}); 
     }); 
     }) 
    }) 
    } 

    filterFiles().then(function(data){ 
    console.log(data); 
    var obj = { 
     fileArray: fileArray, 
     dirArray: dirArray 
    }; 
    res.send(obj); 
    }) 
+0

Vermeiden Sie die [ 'Promise' Konstruktor antipattern] (https://stackoverflow.com/q/23803743/1048572?Was-ist-die-Promose-konstruktion-antipattern-und-wie-zu-vermeiden-sie) und nie 'neue Promise'-Aufrufe verschachteln! – Bergi

+1

Ihre 'Promise.all (actions) .then (function (resolve, reject))' funktioniert nicht, weil '.then' nur einen Parameter übergibt, der das Ergebnis der vorherigen Versprechung ist. In der vierten Zeile verwenden Sie 'console.log' anstelle von' reject' mit dem Fehler. Sie behandeln auch keinen Fehler von 'fs.stat', der passieren kann, wenn eine Datei nicht existiert. – PaulBGD

+0

In 'task' gibt es viele' else' Fälle wo das Versprechen nie gelöst wird – Bergi

Antwort

1

Es sieht mindestens drei Fehler:

  1. Wenn Sie diesen Hit if Anweisung if(! /^\..*/.test(file)){ und den if Block nicht ausgeführt wird, dann das Elternversprechen ist nie erledigt.

  2. Es gibt keine Fehlerbehandlung unter fs.stat(). Wenn Sie bei diesem Aufruf einen Fehler erhalten, ignorieren Sie dies und versuchen, einen ungültigen Wert zu verwenden.

  3. Die Fehlerbehandlung bei Ihrem Aufruf an fs.readdir() ist unvollständig und wird Sie mit einem Versprechen verlassen, das nie abgerechnet wird (wenn es abgelehnt werden sollte).


Für eine robuste Lösung, die Sie wirklich wollen nicht in der gleichen Code-Misch Versprechungen und Rückrufe sein. Es führt zu der Möglichkeit für viele Fehler, insbesondere bei der Fehlerbehandlung (wie Sie sehen können, hatten Sie mindestens drei Fehler - von denen zwei Fehlerbehandlung waren).

Wenn Sie Promises verwenden möchten, promi- sieren Sie die asynchronen Vorgänge, die Sie auf der untersten Ebene verwenden, und verwenden nur Versprechungen, um Ihren asynchronen Codefluss zu steuern. Die einfachste Möglichkeit, die relevanten fs-Operationen zu Promis zu promoten, ist die Verwendung der Bluebird-Promise-Bibliothek mit der Promise.promisifyAll(). Sie müssen diese Bibliothek nicht verwenden. Sie könnten stattdessen manuell Zusicherungswrapper für die von Ihnen verwendeten asynchronen Vorgänge schreiben.

Hier ist eine Version des Codes die Drossel Versprechen Bibliothek:

const Promise = require('bluebird'); 
const fs = Promise.promisifyAll(require('fs')); 

function filterFiles() { 
    return fs.readdirAsync(rootDir).then(function(files) { 
     let fileArray = []; 
     let dirArray = []; 

     // filter out entries that start with . 
     files = files.filter(function(f) { 
      return !f.startsWith("."); 
     }); 
     return Promise.map(files, function(f) { 
      return fs.statAsync(f).then(function(stats) { 
       if (stats.isDirectory()) { 
        dirArray.push(f); 
       } else { 
        fileArray.push(f); 
       } 
      }); 
     }).then(function() { 
      // make the resolved value be an object with two properties containing the arrays 
      return {dirArray, fileArray}; 
     }); 
    }); 
} 



filterFiles().then(function(data) { 
    res.json(data); 
}).catch(function(err) { 
    // put whatever is appropriate here 
    res.status(500).end(); 
}); 

Dies wurde mit diesen Änderungen neu geschrieben/neu strukturiert:

  1. Verwenden Versprechen für alle Asynchron-Operationen
  2. Fix alle Fehler Handhabung, um das zurückgegebene Versprechen abzulehnen
  3. Filtern Sie Dateien mit einer . synchron vor der Verarbeitung von Dateien (s impliziert asynchrone Verarbeitung).
  4. Verwenden Sie Promise.map(), um ein Array von Werten parallel zu verarbeiten.
  5. Im filterFiles().then() Handler Fehler behandeln
  6. Sie können nicht res.send() ein Javascript-Objekt so habe ich res.json(data) statt (obwohl ich nicht sicher bin, was genau wirklich senden möchten Sie).
  7. Ersetzen Regex-Vergleich mit effizienter und einfacher zu verstehen .startsWith().

Wenn Sie die Drossel Versprechen Bibliothek nicht verwenden möchten, können Sie Ihre eigenen Versprechen Wrapper für die fs Methoden, die Sie wie folgt verwenden machen:

fs.readdirAsync = function(dir) { 
    return new Promise(function(resolve, reject) { 
     fs.readdir(dir, function(err, data) { 
      if (err) { 
       reject(err); 
      } else { 
       resolve(data); 
      } 
     }); 
    }); 
} 

fs.statAsync = function(f) { 
    return new Promise(function(resolve, reject) { 
     fs.stat(f, function(err, data) { 
      if (err) { 
       reject(err); 
      } else { 
       resolve(data); 
      } 
     }); 
    }); 
} 

function filterFiles() { 
    return fs.readdirAsync(rootDir).then(function(files) { 
     let fileArray = []; 
     let dirArray = []; 

     // filter out entries that start with . 
     files = files.filter(function(f) { 
      return !f.startsWith("."); 
     }); 
     return Promise.all(files.map(function(f) { 
      return fs.statAsync(f).then(function(stats) { 
       if (stats.isDirectory()) { 
        dirArray.push(f); 
       } else { 
        fileArray.push(f); 
       } 
      }); 
     })).then(function() { 
      // make the resolved value be an object with two properties containing the arrays 
      return {dirArray, fileArray}; 
     }); 
    }); 
} 


filterFiles().then(function(data) { 
    res.json(data); 
}).catch(function(err) { 
    res.status(500).end(); 
}); 
+0

Ich habe auch eine Nicht-Bluebird-Version des Codes hinzugefügt. – jfriend00

+0

Mann! Was ist deine Adresse? Ich schicke dir Bier! Es hat funktioniert, danke Mann! –

+0

@JhamarS. - Froh, dass ich helfen konnte. Ich hoffe, Sie können sehen, wie das Steuern aller Logik mit Versprechen die Dinge vereinfachen und eine robustere Fehlerbehandlung ermöglichen kann. – jfriend00

0

Das Hauptproblem, das Sie haben, ist, dass äußerste Versprechen nicht aufgelöst oder zurückgewiesen wird. Sie können dies beheben, indem Sie Ihre Promise.all auflösen, anstatt sie zurückzugeben.

resolve(
     Promise.all(actions) 
     .then(function(resolvedTasks){ 
      // ... next potential issue is here 
      return {dirArray: dirArray, fileArray: fileArray} 
     }) 
    ); 

(Ich weiß, Art ungeschickt aussehende oder?)

Als nächstes Ihr Rückgabewert nach dem Promise.all behebt, ist ein wenig seltsam. In der task Funktion schieben Sie Elemente auf dirArray und fileArray, aber sie sind nicht in Ihrem Snippet deklariert oder zugewiesen. Ich gehe davon aus, dass sie für diesen Code in Reichweite sind. In diesem Fall müssen Sie nur das gewünschte Objekt zurückgeben.

Zusätzlich Ihre async Code besser lesbar zu machen, sind hier einige Tipps:

  • versuchen nicht Rückrufe mit Versprechungen
  • ein Versprechen Bibliothek verwenden zu mischen beliebigen Code auf Rückrufe beschränkt auf promisify. Beispiel: bluebird's promisifyAll
  • vermeiden Verschachtelung Rückrufe/Versprechen, wenn möglich
Verwandte Themen