即使在Node.js中使用回调函数后,函数的行为也会异步 [英] Function behaving asynchronously even after a callback function in Node.js

查看:64
本文介绍了即使在Node.js中使用回调函数后,函数的行为也会异步的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图使用Node.js的'fs'模块制作文件浏览器。



我编写了以下函数,该函数采用路径并存储该路径(文件/文件夹)的内容在数组中,然后使用回调将其发送回去。

  listDir:function(path ,myCallback){
var resultObj = [];

fs.readdir(path,function(err,data){
console.log('In listDir');
if(err){
return myCallback (err,null);
} else {

data.forEach(function(value){

fs.lstat(path +'/'+ value,函数(错误,统计信息){

if(错误){
console.log(err);
} else {
//console.log('Size -'+ stats.isFile()+'\n');
if(stats.isFile()){
//console.log('is file ---- \n' );
/*resultObj.push({
type:'file',
name:value,
size:stats ['size']
}); * /
resultObj.push(value);
console.log(resultObj +'\ n’);

}否则if(stats.isDirectory()){
//console.log('is folder ---- \n');
/*resultObj.push({
type:'folder',
name:value,
size:stats ['size']
}); * /
resultObj.push(value);
console.log(resultObj +’\n’);
}
}
});

});
console.log(’Resultant obj-’+ resultObj);
return myCallback(null,resultObj);
}
});

}

对于数据中存储的每个文件/文件夹,i正在尝试检查它是文件还是文件夹,并根据我想将一个对象推入我的 resultObj数组。
但是,即使在forEach完成之前也要执行return语句,因此每次都得到一个空的resultObj。



为什么会这样?为什么即使我提供了回调函数,它也仍然异步运行?



当我使用路径参数调用/ test路由时,输出是这样的p>

在listDir

结果obj-

GET / test?path = F:\dummyFolder 304 15.210 ms--

file1

file1,file2

file1,file2,folder3



请帮助我这一点,我真的很难完成,必须在今晚之前完成。并且在作业请勿使用 fs模块的任何同步功能中特别提到了这一点。
那么,我该如何牢记该子句呢?



PS:不要介意这些注释,我将它们用于调试。

解决方案

问题是您没有等待 fs.lstat()函数完成每个文件。您正在排队所有 fs.lstat()调用,但不等待所有这些调用完成。当它仍为 [] 时,您将返回 resultObj



您可以通过在 data.forEach()的回调中添加检查来解决此问题,以查看有多少项目具有该回调 fs.lstat()被调用。

  listDir:(path,myCallback )=> {
let resultObj = [];

fs.readdir(path,(err,data)=> {
console.log('In listDir');
if(err){
return myCallback(err);
}

let itemsCompleted = 0
data.forEach(value => {
fs.lstat(path +'/'+ value,(err,stats)=> {
itemsCompleted ++
if(err){
console.log(err);
} else {
if(stats .isFile()|| stats.isDirectory()){
resultObj.push(value);
console.log(resultObj +'\n');
}
}

if(itemsCompleted> = data.length){
return myCallback(null,resultObj)
}
});
}); $ b b $ b}

但是,上述方法仍然比 async / await 。使用 async / await 的承诺目前是首选方法处理Node中的异步控制流。使用 util.promisify() 表示 fs.readdir() fs.lstat()使我们能够简化控制流程并显着提高 listDir()的可读性。此外,通过使用 map()代替 forEach(),我们可以使用 Promise.all ()为每个条目包装从 lstatAsync()返回的Promise。

  const {promisify} = require('util')
const fs = require('fs')
const {join} = require('path')
const readdirAsync = promisify(fs.readdir)
const lstatAsync = promisify(fs.lstat)

module.exports = {
listDir:异步路径=> {
let resultObj = [];

让条目=等待readdirAsync(path)
console.log(’在listDir’);

等待Promise.all(entries.map(异步值=> {
试试{
让统计信息=等待lstatAsync(join(path,value))

if(stats.isFile()|| stats.isDirectory()){
resultObj.push(value);
console.log(resultObj +'\n');
}
} catch(err){
console.log(err)
}
}))

返回结果Obj
}
}


I am attempting to make a file explorer using the 'fs' module of Node.js.

I wrote the following function which takes in a path and stores the contents of that path(files/folders) in an array and just sends it back using the callback.

listDir: function (path, myCallback) {
    var resultObj = [];

    fs.readdir(path, function (err, data) {
        console.log('In listDir');
        if (err) {
            return myCallback(err, null);
        } else {

            data.forEach(function (value) {

                fs.lstat(path + '/' + value, function (err, stats) {

                    if (err) {
                        console.log(err);
                    } else {
                        //console.log('Size-' + stats.isFile()+'\n');
                        if(stats.isFile()){
                            //console.log('is file----\n');
                            /*resultObj.push({
                                type: 'file',
                                name: value,
                                size: stats['size']
                            });*/
                            resultObj.push(value);
                            console.log(resultObj+'\n');

                        } else if(stats.isDirectory()){
                            //console.log('is folder----\n');
                            /*resultObj.push({
                                type: 'folder',
                                name: value,
                                size: stats['size']
                            });*/
                            resultObj.push(value);
                            console.log(resultObj+'\n');
                        }
                    }
                });

            });
            console.log('Resultant obj-' + resultObj);
            return myCallback(null, resultObj);
        }
    });

}

For each file/folder stored in 'data', i'm trying to check whether it is a file or a folder and depending on that i want to push an object into my 'resultObj' array. However, the return statement gets executed even before the forEach is complete and hence i get an empty resultObj everytime.

Why does that happen ? Why does it behave asynchronously even when i have provided a callback function ?

The output is something like this when i call the /test route with the path param--

In listDir
Resultant obj-
GET /test?path=F:\dummyFolder 304 15.210 ms - -
file1
file1,file2
file1,file2,folder3

Please help me with this, i'm really stuck with this assignment which has to be completed by tonight. And also it has been specifically mentioned in the assignment NOT TO USE ANY SYNCHRONOUS FUNCTION OF THE 'fs' MODULE. So how can i do this keeping that clause in mind ?

PS: Don't mind the comments, i used them for debugging.

解决方案

The problem is that you're not waiting for the fs.lstat() function to finish for each file. You're queueing up all the fs.lstat() calls but then not waiting for all of them to finish. You're just going to return back resultObj when it is still [].

You can fix this by adding a check inside the callback of your data.forEach() to see how many of the items have had the callback for fs.lstat() invoked.

listDir: (path, myCallback) => {
    let resultObj = [];

    fs.readdir(path, (err, data) => {
      console.log('In listDir');
      if (err) {
          return myCallback(err);
      }

      let itemsCompleted = 0        
      data.forEach(value => {
        fs.lstat(path + '/' + value, (err, stats) => {
          itemsCompleted++
          if (err) {
            console.log(err);
          } else {
            if(stats.isFile() || stats.isDirectory()){
              resultObj.push(value);
              console.log(resultObj+'\n');
            }
          }

          if(itemsCompleted >= data.length) {
            return myCallback(null, resultObj)
          }
        });            
    });
}

However, the above approach is still more complicated than Promises with async/await. Promises with async/await is currently the preferred way to handle asynchronous control flow in Node. Utilizing util.promisify() to promisify fs.readdir() and fs.lstat() allows us to flatten the control flow and dramatically improve the readability of the listDir(). Additionally, by using map() instead of forEach() we can use Promise.all() to wrap the returned Promise from lstatAsync() for each entry.

const {promisify} = require('util')
const fs = require('fs')
const {join} = require('path')
const readdirAsync = promisify(fs.readdir)
const lstatAsync = promisify(fs.lstat)

module.exports = {
  listDir: async path => {
    let resultObj = []; 

    let entries = await readdirAsync(path)
    console.log('In listDir');

    await Promise.all(entries.map(async value => {
      try {
        let stats = await lstatAsync(join(path, value))

        if(stats.isFile() || stats.isDirectory()){
          resultObj.push(value);
          console.log(resultObj+'\n');
        }
      } catch (err) {
        console.log(err)
      }
    }))

    return resultObj
  }
}

这篇关于即使在Node.js中使用回调函数后,函数的行为也会异步的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆