前言

我们以查找指定目录下的最大文件为例,感受从

回调函数 -> Promise -> Generator -> Async

异步处理方式的改变。

API 介绍

为了实现这个功能,我们需要用到几个 Nodejs 的 API,所以我们来简单介绍一下。

fs.readdir

readdir 方法用于读取目录,返回一个包含文件和目录的数组。

fs.stat

stat 方法的参数是一个文件或目录,它产生一个对象,该对象包含了该文件或目录的具体信息。此外,该对象还有一个 isFile() 方法可以判断正在处理的到底是一个文件,还是一个目录。

思路分析

我们基本的实现思路就是:

  • fs.readdir 获取指定目录的内容信息
  • 循环遍历内容信息,使用 fs.stat 获取该文件或者目录的具体信息
  • 将具体信息储存起来
  • 当全部储存起来后,筛选其中的是文件的信息
  • 遍历比较,找出最大文件
  • 获取并返回最大文件
    然后我们直接上代码吧。

回调函数

  1. var fs = require('fs');
  2. var path = require('path');
  3.  
  4. function findLargest(dir, cb) {
  5. // 读取目录下的所有文件
  6. fs.readdir(dir, function(er, files) {
  7. if (er) return cb(er);
  8.  
  9. var counter = files.length;
  10. var errored = false;
  11. var stats = [];
  12.  
  13. files.forEach(function(file, index) {
  14. // 读取文件信息
  15. fs.stat(path.join(dir, file), function(er, stat) {
  16.  
  17. if (errored) return;
  18.  
  19. if (er) {
  20. errored = true;
  21. return cb(er);
  22. }
  23.  
  24. stats[index] = stat;
  25.  
  26. // 事先算好有多少个文件,读完 1 个文件信息,计数减 1,当为 0 时,说明读取完毕,此时执行最终的比较操作
  27. if (--counter == 0) {
  28.  
  29. var largest = stats
  30. .filter(function(stat) { return stat.isFile() })
  31. .reduce(function(prev, next) {
  32. if (prev.size > next.size) return prev
  33. return next
  34. })
  35.  
  36. cb(null, files[stats.indexOf(largest)])
  37. }
  38. })
  39. })
  40. })
  41. }

使用方式为:

  1. // 查找当前目录最大的文件
  2. findLargest('./', function(er, filename) {
  3. if (er) return console.error(er)
  4. console.log('largest file was:', filename)
  5. });

Promise

  1. var fs = require('fs');
  2. var path = require('path');
  3.  
  4. var readDir = function(dir) {
  5. return new Promise(function(resolve, reject) {
  6. fs.readdir(dir, function(err, files) {
  7. if (err) reject(err);
  8. resolve(files)
  9. })
  10. })
  11. }
  12.  
  13. var stat = function(path) {
  14. return new Promise(function(resolve, reject) {
  15. fs.stat(path, function(err, stat) {
  16. if (err) reject(err)
  17. resolve(stat)
  18. })
  19. })
  20. }
  21.  
  22. function findLargest(dir) {
  23. return readDir(dir)
  24. .then(function(files) {
  25. let promises = files.map(file => stat(path.join(dir, file)))
  26. return Promise.all(promises).then(function(stats) {
  27. return { stats, files }
  28. })
  29. })
  30. .then(data => {
  31.  
  32. let largest = data.stats
  33. .filter(function(stat) { return stat.isFile() })
  34. .reduce((prev, next) => {
  35. if (prev.size > next.size) return prev
  36. return next
  37. })
  38.  
  39. return data.files[data.stats.indexOf(largest)]
  40. })
  41.  
  42. }

使用方式为:

  1. findLargest('./')
  2. .then(function(filename) {
  3. console.log('largest file was:', filename);
  4. })
  5. .catch(function() {
  6. console.log(error);
  7. });

Generator

  1. var fs = require('fs');
  2. var path = require('path');
  3.  
  4. var co = require('co')
  5.  
  6. var readDir = function(dir) {
  7. return new Promise(function(resolve, reject) {
  8. fs.readdir(dir, function(err, files) {
  9. if (err) reject(err);
  10. resolve(files)
  11. })
  12. })
  13. }
  14.  
  15. var stat = function(path) {
  16. return new Promise(function(resolve, reject) {
  17. fs.stat(path, function(err, stat) {
  18. if (err) reject(err)
  19. resolve(stat)
  20. })
  21. })
  22. }
  23.  
  24. function* findLargest(dir) {
  25. var files = yield readDir(dir);
  26. var stats = yield files.map(function(file) {
  27. return stat(path.join(dir, file))
  28. })
  29.  
  30. let largest = stats
  31. .filter(function(stat) { return stat.isFile() })
  32. .reduce((prev, next) => {
  33. if (prev.size > next.size) return prev
  34. return next
  35. })
  36.  
  37. return files[stats.indexOf(largest)]
  38.  
  39. }

使用方式为:

  1. co(findLargest, './')
  2. .then(function(filename) {
  3. console.log('largest file was:', filename);
  4. })
  5. .catch(function() {
  6. console.log(error);
  7. });

Async

  1. var fs = require('fs');
  2. var path = require('path');
  3.  
  4. var readDir = function(dir) {
  5. return new Promise(function(resolve, reject) {
  6. fs.readdir(dir, function(err, files) {
  7. if (err) reject(err);
  8. resolve(files)
  9. })
  10. })
  11. }
  12.  
  13. var stat = function(path) {
  14. return new Promise(function(resolve, reject) {
  15. fs.stat(path, function(err, stat) {
  16. if (err) reject(err)
  17. resolve(stat)
  18. })
  19. })
  20. }
  21.  
  22. async function findLargest(dir) {
  23. var files = await readDir(dir);
  24.  
  25. let promises = files.map(file => stat(path.join(dir, file)))
  26. var stats = await Promise.all(promises)
  27.  
  28. let largest = stats
  29. .filter(function(stat) { return stat.isFile() })
  30. .reduce((prev, next) => {
  31. if (prev.size > next.size) return prev
  32. return next
  33. })
  34.  
  35. return files[stats.indexOf(largest)]
  36.  
  37. }

使用方式为:

  1. findLargest('./')
  2. .then(function(filename) {
  3. console.log('largest file was:', filename);
  4. })
  5. .catch(function() {
  6. console.log(error);
  7. });

ES6 系列

ES6 系列目录地址:https://github.com/mqyqingfeng/Blog

ES6 系列预计写二十篇左右,旨在加深 ES6 部分知识点的理解,重点讲解块级作用域、标签模板、箭头函数、Symbol、Set、Map 以及 Promise 的模拟实现、模块加载方案、异步处理等内容。

如果有错误或者不严谨的地方,请务必给予指正,十分感谢。如果喜欢或者有所启发,欢迎 star,对作者也是一种鼓励。