2017-07-25 142 views
0

我有許多函數可以在循環中爲不同的數據執行幾次異步函數。我要等到所有異步功能將被執行,然後解析(),(或稱非承諾函數回調函數)等待很多異步函數執行

var readFiles =()=>{ 
    return new Promise((resolve,reject)=>{ 
    var iterator = 0; 
    var contents = {}; 
    for(let i in this.files){ 
     iterator++; 
     let p = path.resolve(this.componentPath,this.files[i]); 
     fs.readFile(p,{encoding:'utf8'},(err,data)=>{ 
     if(err){ 
      reject(`Could not read ${this.files[i]} file.`); 
     } else { 
      contents[this.files[i]] = data; 
      iterator--; 
      if(!iterator) resolve(contents); 
     } 
     }); 
    } 
    if(!iterator) resolve(contents); //in case of !this.files.length 
    }); 
}; 

我增加iterator在每個循環重複,那麼,在異步函數的回調降低iterator並檢查是否所有的異步函數都已完成(迭代器=== 0),如果是這樣 - 請致電resolve()

它很好用,但看起來不夠優雅和可讀。你知道這個問題有更好的辦法嗎?

+3

[Promise.all()](HTTPS://developer.mozilla。 org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all)將非常合適!而不是一個大的承諾,將個體較小的承諾推向一個數組。然後,使用'承諾。all()'等待所有的承諾解決。 – dpaulus

+0

我不知道這個功能,謝謝! –

+2

請注意,你正在做的是解決這個問題的正確方法;你的編碼器本能就是關鍵。 'Promise.all'將簡單地爲你做好準備,保持你的代碼清潔。另外 - 你可能想使用'fs-extra',一個替代'fs'的插件,但是增加了promise的支持。 – Amadan

回答

2

繼續註釋一些代碼和更多細節!

Promise.all()需要一個迭代器,並等待所有承諾解決或拒絕。它會返回所有承諾的結果。因此,不要跟蹤所有承諾何時解決,我們可以創建一些承諾並將其添加到數組中。然後,使用Promise.all()等待它們全部解決。

const readFiles =() => { 
    const promises = []; 

    for(let i in files) { 
    const p = path.resolve(componentPath, files[i]); 

    promises.push(new Promise((resolve, reject) => { 
     fs.readFile(p, {encoding:'utf8'}, (err, data) => { 
     if(err) { 
      reject(`Could not read ${files[i]} file.`); 
     } else { 
      resolve(data); 
     } 
     }); 
    })); 
    } 

    return Promise.all(promises); 
}; 

const fileContents = readFiles().then(contents => { 
    console.log(contents) 
}) 
.catch(err => console.error(err)); 
1

你只需要推動所有的承諾變成一個數組,然後把它作爲參數傳遞給Promise.all(arrayOfPromises)

嘗試這樣的事:

var readFiles =() => { 
    var promises = []; 
    let contents = {}; 
    var keys_files = Object.keys(this.files); 
    if (keys_files.length <= 0) { 
    var promise = new Promise((resolve, reject) => { 
    resolve(contents); 
    }); 
    promises.push(promise); 
    } 

    keys_files.forEach((key) => { 
    var file = this.files[key]; 
    var promise = new Promise((resolve, reject) => { 
     const currentPath = path.resolve(this.componentPath, file); 
     fs.readFile(p,{encoding:'utf8'},(err, data) => { 
     if (err) { 
      return reject(`Could not read ${file} file.`); 
     } 

     contents[file] = data; 
     resolve(contents) 
     }); 
    }); 
    }); 

    return Promises.all(promises); 
} 

那麼你應該使用功能類似所以:

// this will return a promise that contains an array of promises 
var readAllFiles = readFiles(); 
// the then block only will execute if all promises were resolved if one of them were reject so all the process was rejected automatically 
readAllFiles.then((promises) => { 
    promises.forEach((respond) => { 
     console.log(respond); 
    }); 
}).catch((error) => error); 

如果你不介意的承諾之一被拒絕,也許你應該做到以下幾點

var readFiles =() => { 
    var promises = []; 
    let contents = {}; 
    var keys_files = Object.keys(this.files); 
    if (keys_files.length <= 0) { 
    var promise = new Promise((resolve, reject) => { 
    resolve(contents); 
    }); 
    promises.push(promise); 
    } 

    keys_files.forEach((key) => { 
    var file = this.files[key]; 
    var promise = new Promise((resolve, reject) => { 
     const currentPath = path.resolve(this.componentPath, file); 
     fs.readFile(p,{encoding:'utf8'},(err, data) => { 
     // create an object with the information 
     let info = { completed: true }; 
     if (err) { 
      info.completed = false; 
      info.error = err; 
      return resolve(info); 
     } 

     info.data = data; 
     contents[file] = info; 
     resolve(contents) 
     }); 
    }); 
    }); 

    return Promises.all(promises); 
} 
+0

感謝您的努力! –

1

從評論複製:

而且 - 你可能想使用fs-extra,一直接替換fs,但添加了承諾支持。

這裏是如何繼續下去:

const fs = require('fs-extra'); 
var readFiles =()=>{ 
    let promises = files 
    .map(file => path.resolve(componentPath, file)) 
    .map(path => fs.readFile(path)); 
    return Promise.all(promises); 
}); 

尼斯和清潔。然後,您可以獲取內容是這樣的:

readFiles() 
.then(contents => { ... }) 
.catch(error => { ... }); 

這將在第一個錯誤,雖然失敗(因爲這是Promise.all一樣)。如果你想個別的錯誤處理,你可以添加其他map行:

.map(promise => promise.catch(err => err)); 

然後你就可以篩選結果:

let errors = contents.filter(content => content instanceof Error) 
let successes = contents.filter(content => !(content instanceof Error)) 
+0

確實看起來很乾淨! :) 非常感謝 –