2014-02-13 49 views
2

我想了解異步編程Node.js,但停滯在這段代碼。在JavaScript中使每個異步

此功能在其回調返回文件的數組中的目錄:

function openDir(path, callback) { 
    path = __dirname + path; 
    fs.exists(path, function (exists) { 
     if (exists) { 
      fs.readdir(path, function (err, files) { 
       if (err) { 
        throw err; 
       } 
       var result = []; 
       files.forEach(function (filename, index) { 
        result[index] = filename; 
       }); 
       return callback(result); 
      }); 
     } 
    }); 
} 

但是當我使用內部.forEach異步代碼,它沒有返回值:

function openDir(path, callback) { 
    path = __dirname + path; 
    fs.exists(path, function (exists) { 
     if (exists) { 
      fs.readdir(path, function (err, files) { 
       if (err) { 
        throw err; 
       } 
       var result = []; 
       files.forEach(function (filename, index) { 
        fs.stat(path + filename, function (err, stats) { 
         if (err) { 
          throw err; 
         } 
         result[index] = filename; 
        }); 
       }); 
       return callback(result); 
      }); 
     } 
    }); 
} 

我明白爲什麼會發生,但不明白如何編寫正確的代碼。

+1

您不能從異步函數返回,您必須設置回調 –

+0

此模塊將使您的生活更輕鬆:https://github.com/caolan/async – joao

+0

回調或承諾/延期模式是您的方式到這裏。 – dfsq

回答

2

其他答案可能效果不錯,但它們目前在語義上與原始代碼完全不同:它們都執行stats並行,而不是順序。 forEach將啓動儘可能多的異步stats操作,因爲文件列表中有文件。這些操作的完成順序可能與列表的原始順序完全不同。這可能會嚴重影響錯誤處理邏輯。

以下方法實現了一個狀態機,其目的是,以異步方式執行stats,但順序地(未測試):

function openDir(path, callback) { 
    path = __dirname + path; 
    fs.exists(path, function (exists) { 
     if (!exists) 
      callback(null, null); // node (err, result) convention 
     else { 
      fs.readdir(path, function (err, files) { 
       if (err) 
        callback(err, null); // node (err, result) convention 
       else { 
        var results = []; 
        var i = 0; 
        nextStep(); // process the first file (the first step) 

        function nextStep() { 
         if (i >= files.length) // no more files? 
          callback(null, result); // node (err, result) convention 
         else { 
          fs.stat(path + files[i], function (err, stats) { 
           if (err) 
            callback(err, null); // node (err, result) convention 
           else { 
            results[i++] = stats; 
            // proceed to the next file 
            nextStep(); 
           } 
          }); 
         } 
        } 
       } 
      } 
     } 
    });     
}); 

Promises可有助於減少的著名"Pyramid of Doom"類似上面的嵌套級別。

+1

承諾,或異步模塊:) –

+1

@AndréAlçadaPadez,的確,這是很好的選擇:)此演示文稿可能有助於使它:http://trevorburnham.com/presentations/flow-control-with-promises/ – Noseratio

5

問題是fs.stat也是異步的,但你也許可以這樣做:

var result = [], 
    expectedLoadCount = files.length, 
    loadCount = 0; 

files.forEach(function (filename, index) { 
    fs.stat(path + filename, function (err, stats) { 
     if (err) { 
      throw err; 
     } 
     result[index] = filename; 
     if (++loadCount === expectedLoadCount) callback(result); 
    }); 
}); 
1

試試這個:

function openDir(path, callback) { 
    path = __dirname + path; 
    fs.exists(path, function (exists) { 
     var totalFiles = 0;; 
     if (exists) { 
      fs.readdir(path, function (err, files) { 
       if (err) { 
        throw err; 
       } 
       var result = []; 
       files.forEach(function (filename, index) { 
        fs.stat(path + filename, function (err, stats) { 
         if (err) { 
          throw err; 
         } 
         result[index] = filename; 
         totalFiles++; 
         if(totalFiles === files.length){ 
          callback(result); 
         } 
        }); 
       }); 
      }); 
     } 
    }); 
} 

,你也可以使用Async module,以幫助這些類型的情況下

+1

我在2分鐘前擊敗了你= P – plalx

+0

對你有好處,但是你沒有提到Async模塊:P –

+1

我在我的項目的許多部分使用Async。它非常有用,也是有史以來最好的節點包之一。 – Ito