Вызов асинхронных рекурсивных функций в Node.js с Promise - PullRequest
1 голос
/ 13 октября 2019

сначала мне нужно сказать, что я довольно новичок в себе как в электроне, так и в node.js.

Что я делаю, так это то, что я пытаюсь отправить массив с некоторыми данными каталога в мой браузер и этот асинхронный (Ошибка: массивпусто).

Проблемой должна быть моя функция 'scanDirectories (path)'. Если я сделаю его нерекурсивным (scanDirectories (res) -> only return res), он будет работать довольно хорошо для каталогов 1 уровня, но не рекурсивно.

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

Надеюсь, вы можете помочь мне.
Саймон

main.js:
вызов функции изipcmain

ipcMain.on('fileTree', (event, arg) => {
  let fileDirectory = helperFile.getDirectories(arg);
  fileDirectory.then(function(result) {
    event.reply('fileTree', result);
  }, function(err) {
    console.log(err);
  })
})

files.js

const { readdir } = require('fs').promises;
const resolvePath = require('path').resolve;

module.exports = {
    getDirectories: async function(path) {
       return new Promise(function (resolve, reject) {
           try {
               resolve(scanDirectories(path));
           } catch {
               reject('Error');
           }
       });
    }
};

async function scanDirectories(path) {
    const dirents = await readdir(path, {withFileTypes: true});
    const files = dirents.map((dirent) => {
        const res = resolvePath(path, dirent.name);
        return dirent.isDirectory() ? scanDirectories(res) : res;
    });
    return Array.prototype.concat(...files);
}

Ответы [ 2 ]

1 голос
/ 13 октября 2019

Вы можете попробовать что-то вроде этого, чтобы сгенерировать массив обещаний:

files.js

const { readdir } = require('fs').promises;
const resolvePath = require('path').resolve;

module.exports = {
  // This is an async function so don’t need internal promise
  getDirectories: async function(path) {
    try {
      const dirs = await scanDirectories(path);
      return dirs;
    }
    catch {
      throw new Error('Error');
    }
  }
};

async function scanDirectories(path) {
  const dirents = await readdir(path, {withFileTypes: true});

  // Generate array of promises
  const promises = dirents.map(dirent => {
    const res = resolvePath(path, dirent.name);
    return dirent.isDirectory()
      ? scanDirectories(res)
      : Promise.resolve(res);
  });

  // Await all promises
  const files = await Promise.all(promises);
  return Array.prototype.concat(...files);
}
0 голосов
/ 13 октября 2019

Если вы вызываете функцию async без await, вы получаете обещание.

Ваш обработчик событий обрабатывает этот тип OK с then (у него проблемы с обработкой ошибок),но ваш рекурсивный вызов scanDirectories этого не делает.

Самый простой способ дождаться разрешения асинхронной функции - это использовать await.

Так что это изменение делает рекурсивный вызов правильно:

return dirent.isDirectory() ? (await scanDirectories(res)) : res;

Обратите внимание на добавление await.

Однако «Array.map» не предназначен для использования в асинхронных функциях. Он будет вызывать их синхронно и создавать массив обещаний. Не то, что вы хотите.

Кроме того, этот код неоправданно сложен, заключая обещание в обещание и используя try/catch способом, который не будет работать:

    getDirectories: async function(path) {
       return new Promise(function (resolve, reject) {
           try {
               resolve(scanDirectories(path));
           } catch {
               reject('Error');
           }
       });
    }

ПростоВызовите scanDirectories непосредственно из вашего исходного обработчика событий и сделайте обработчик событий функцией async, так что большая часть кода просто исчезнет.

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

Вот ваш исходный код, упрощенный и исправленный, но работающий в основном так жепуть:

ipcMain.on('fileTree', async (event, arg) => {
  try {
    event.reply('fileTree', await helperFile.scanDirectories(arg);
  } catch (e) {
    console.log(e);
  }
});

// files.js

const { readdir } = require('fs').promises;
const resolvePath = require('path').resolve;

module.exports = {
  scanDirectories: async function(path) {
    const dirents = await readdir(path, { withFileTypes: true });
    const files = [];
    for (const dirent of dirents) {
      const res = resolvePath(path, dirent.name);
      if (dirent.isDirectory()) {
        files = files.concat(await scanDirectories(res));
      } else {
         files.push(res);
      }
    });
    return files;
  }
}
...