2017-06-19 163 views
0

我是新來的節點,我遇到了解決異步承諾的問題。我的承諾沒有解決,我不確定我做錯了什麼。我仍然無法理解承諾和回調,所以任何反饋都是有幫助的。承諾不會解決

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

避免['Promise'構造函數反模式](https://stackoverflow.com/q/23803743/1048572?What-is-the-promise-construction-antipattern-and-how-to-avoid-it),永遠不會嵌套'新的Promise'調用! – Bergi

+1

你的'Promise.all(actions).then(function(resolve,reject))'不起作用,因爲'.then'只傳遞一個參數,這個參數是前一個promise的結果。同樣,在第四行中,您使用'console.log'而不是調用帶有錯誤的'reject'。你也不會處理'fs.stat'中的一個錯誤,當文件不存在時可能發生錯誤。 – PaulBGD

+0

在'task'中有許多'else'的情況,其中承諾永遠不會解決 – Bergi

回答

1

它看到至少有三個錯誤:

  1. 當你點擊這個if聲明if(! /^\..*/.test(file)){,它不執行if塊,然後父母的承諾永遠不會解決。

  2. fs.stat()上沒有錯誤處理,所以如果您在該調用中出現錯誤,您將忽略該錯誤並嘗試使用錯誤的值。

  3. 您致電fs.readdir()時的錯誤處理不完整,並且會給您帶來一個未解決的承諾(何時應該被拒絕)。


對於一個強大的解決方案,你真的不希望在相同的代碼混合承諾和回調。這會導致出現很多錯誤的機會,特別是在錯誤處理方面(因爲您可以看到您至少有三個錯誤 - 其中兩個錯誤處理錯誤)。

如果您打算使用Promises,那麼promisify您在最低級別使用的異步操作,並只使用promise來控制異步代碼流。我知道最簡單的方法來提供相關的fs操作,就是使用藍鳥承諾庫及其Promise.promisifyAll()。您不必使用該庫。您可以改爲手動編寫您正在使用的異步操作的承諾包裝器。

下面是一個使用藍鳥承諾庫中代碼的版本:

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(); 
}); 

這是改寫/這些變化重組:

  1. 使用承諾對所有異步操作
  2. 修正所有錯誤處理以拒絕返回的承諾
  3. 在處理任何文件之前同步過濾掉以.開頭的文件(s表明異步處理)。使用Promise.map()並行處理一個數組數組。
  4. filterFiles().then()處理程序,處理錯誤
  5. 你不能res.send()一個JavaScript對象,所以我用res.json(data)代替(雖然我不知道究竟你真的想送)。
  6. 替換正則表達式比較效率更高,更易於理解.startsWith()

如果你不想使用藍鳥承諾庫,你可以讓自己的諾言包裝爲你使用這樣的方法FS:

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

我也加了一個非Bluebird版本的代碼。 – jfriend00

+0

老兄!我給你送啤酒的地址是什麼?它工作感謝人! –

+0

@JhamarS。 - 很高興我能幫上忙。希望你可以看到如何用promise來控制所有的邏輯可以簡化事物並進行更強大的錯誤處理。 – jfriend00

0

您遇到的主要問題是最外層的Promise未解決或被拒絕。你可以通過解決你的Promise.all而不是返回它來解決這個問題。 (樣的,我知道的,笨拙的外觀吧?)

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

接下來,Promise.all結算後您的返回值是一個有點怪異。在task函數中,您正在將商品推送到dirArrayfileArray,但它們未在代碼段中聲明或分配。我會假設他們在這個代碼範圍內。在這種情況下,你只需要返回你想要的對象。

此外,爲了讓您的異步代碼更易讀,這裏有一些提示:

  • 儘量不要與承諾
  • 使用無極庫promisify限於回調任何代碼混合回調。例如:bluebird's promisifyAll
  • 避免嵌套的回調/承諾在可能的情況