Добавление элементов в глобальный массив из функции обратного вызова и последующий доступ к нему глобально - PullRequest
1 голос
/ 28 сентября 2019

Вот проблема.

У меня есть глобальная переменная (тип массива) с именем папки

let folders = [];

Я изменяю ее внутриФункция обратного вызова внутри еще одной функции обратного вызова.Вот как.

app.get("/", (req, res) => {
// TODO: Proceed only if the path is correct and it is a directory
fs.readdir(dir, (err, files) => {
    console.log("READING:");

    if (err) throw err;

    files.forEach(file => {
        const add = folder => folders.push(folder);
        fs.lstat(path.join(dir, file), (err, stats) => {
            if (err) throw err;

            if (stats.isDirectory()) {
                add(file);
            }
            console.log("INSIDE: " + folders);
        });
        console.log("OUTSITE: " + folders);
    });
});

res.send(folders.length.toString());
});

Теперь проблема в том, что когда я читаю это в этой строке:

res.send(folders.length.toString());

Длина всегда равна 0. И это также 0 в строке журнала консоли.где я печатаю его с помощью ВНЕШНИЙ , но он хорошо читается, когда я печатаю его в строке, где я упоминаю это с ВНУТРИ .

Я знаю проблему после некоторого поиска.Это происходит потому, что обратный вызов устанавливает переменную в более позднее время в цикле событий. (Если это имеет смысл, но вы понимаете, в чем дело).

Я знаю проблему, но у меня нет никакого представления окак это решить.Я пробовал различные реализации, включая добавление глобальной функции, которая помещает в массив, и вызов ее frpm внутри обратного вызова, но результаты такие же.

Вот полный код.

const express = require("express");
const fs = require("fs");
const path = require("path");
const os = require("os");

// Initialize Express
const app = express();

// PORT on which the app process should be started
const PORT = process.env.PORT || 5100;

// Setting Up the path to Projects folder dynamically
// !Currently only works(tested) on the Darwin(MacOS) systems PS. I don't own a Windows
// TODO: Test on Windowsn and Linux
const homedir = os.homedir();
const dir = `${homedir}/Projects/`;

// TODO: Re-Write using Async/Await as it is not fully suppported as of Node version 10.0

let folders = [];

// Home Route
app.get("/", (req, res) => {
    // TODO: Proceed only if the path is correct and it is a directory
    fs.readdir(dir, (err, files) => {
        console.log("READING:");

        if (err) throw err;

        files.forEach(file => {
            const add = folder => folders.push(folder);
            fs.lstat(path.join(dir, file), (err, stats) => {
                if (err) throw err;

                if (stats.isDirectory()) {
                    add(file);
                }
                console.log("INSIDE: " + folders);
            });
            console.log("OUTSITE: " + folders);
        });
    });

    res.send(folders.length.toString());
});

// Start the express server
app.listen(PORT, err => {
    if (err) throw err;
    console.log(`Project Lister Running On PORT: ${PORT}`);
});

Какие-нибудь решения?

Ответы [ 2 ]

1 голос
/ 28 сентября 2019

Проблема здесь в том, что fs.lstat является асинхронным.

Если вы используете синхронизирующую версию fs.lstatSync, то вы можете вызвать res.send после цикла forEach.

app.get("/", (req, res) => {
  // TODO: Proceed only if the path is correct and it is a directory
  fs.readdir(dir, (err, files) => {
      console.log("READING:");

      if (err) throw err;

      files.forEach(file => {
          const add = folder => folders.push(folder);
          try {
            const stats = fs.lstatSync(path.join(dir, file))

            if (stats.isDirectory()) {
              add(file);
          }
          } catch (err) {
            throw err
          }
      });

      res.send(folders.length.toString());
  })
})

Или для неблокирующего способа вы можете использовать Promise.all:

app.get("/", (req, res) => {
  // TODO: Proceed only if the path is correct and it is a directory
  fs.readdir(dir, (err, files) => {
    console.log("READING:");

    if (err) throw err;

    const promises = files.map(file => {
      return new Promise((resolve, reject) => {
        fs.lstat(path.join(dir, file), (err, stats) => {
          if (err) {
            reject(err);
          }
          if (stats.isDirectory()) {
            add(file);
            resolve();
          }
          console.log("INSIDE: " + folders);
        });
      });
    });

    Promise.all(promises, () => {
      res.send(folders.length.toString());
    });
  });
});
0 голосов
/ 29 сентября 2019

Итак, вот самое простое решение, которое я могу найти самостоятельно!

@ Ответ PeterN верен, но может быть трудно обернуть голову новичка!

Вот мой окончательный код.

const express = require("express");
const fs = require("fs").promises; // !IMPORTANT Get the promises version of the File System Module
const path = require("path");
const os = require("os");

// Initialize Express
const app = express();

// PORT on which the app process should be started
const PORT = process.env.PORT || 5100;

// Setting Up the path to Projects folder dynamically
// !Currently only works(tested) on the Darwin(MacOS) systems PS. I don't own a Windows
// TODO: Test on Windows and Linux
const homedir = os.homedir();
const dir = `${homedir}/Projects/`;

// Home Route
app.get("/", async (req, res) => {
    let folders = [];

    // TODO: Proceed only if the path is correct and is a directory
    try {
        let files = await fs.readdir(dir);

        for (let i = 0; i < files.length; i++) {
            let file = await fs.lstat(path.join(dir, files[i]));

            if (file.isDirectory()) {
                folders.push(files[i]);
            }
        }
    } catch (error) {
        console.error(error);
    }

    res.send(folders);
});

// Start the express server
app.listen(PORT, err => {
    if (err) throw err;
    console.log(`Project Lister Running On PORT: ${PORT}`);
});

Обратите внимание, что во второй строке, где я импортирую модули 'fs', я импортирую его по-другому, или, скорее, скажу другую версию!

Iтеперь импортируйте его как:

const fs = require("fs").promises;

.Прочис, добавленный в последний раз, импортирует функции, методы этого модуля в их реализацию на основе Promise.Думаю, вы должны заметить, что он стабилен только в версии 11.x и выше на NodeJ на данный момент.Я использую> 12.x.

Теперь остальная часть процесса довольно проста, если вы знакомы с Async / Await и Promises.А если нет, я бы настоятельно рекомендовал вам заняться этим, поскольку это может спасти ваш день, как это было со мной.

Вот отличное руководство по этому вопросу: Async / Await и Promise в JS

Ps.Используйте цикл for вместо 'array.forEach (e => // Do Something);'подход, поскольку он снова будет представлять ту же проблему, с которой сталкивался ранее, потому что он также основан на обратном вызове!

Надеюсь, я вам помог.Спасибо!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...