javascriptnode.jsasynchronouslunrjs

How to handle async loop?


I'm currently working on a project to implement a full-text search client-side in JavaScript based on lunr.js.

The thing is, I'm struggling in building then saving the index because I have several asynchronous calls.

    function buildIndex(rawIndex, root, indexPath = root + 'js/app/index.json') {
  var path = path || require('path');
  var fs = fs || require('fs'),
    promesses = [],
    ignore = ['node_modules'],
    files = fs.readdirSync(root);
  files.forEach(function (file) {

    if (fs.statSync(path.join(root, file)).isDirectory() && ignore.indexOf(file) == -1) {
      buildIndex(rawIndex, path.join(root, file), indexPath);
    }
    else if (file.substr(-5) === '.html' && file != 'example.html') {
      var promesse = JSDOM.fromFile(path.join(root, file)).then(dom => {

        var $ = require('../lib/_jquery')(dom.window);
        populate();
        console.log(file + " indexé");
        function populate() {
          $('h1, h2, h3, h4, h5, h6').each(function () {
            var title = $(this);
            var link = path.join(root, file).replace('..\\', '') + "#" + title.prop('id');
            var body = title.nextUntil('h1, h2, h3, h4, h5, h6');
            rawIndex.add({
              id: link,
              title: title.text().latinise(),
              body: body.text().latinise()
            });
          });
        };
      });
      promesses.push(promesse);
    }
  });
  Promise.all(promesses)
    .then(function () {
      fs.writeFileSync(indexPath, "var data = " + JSON.stringify(rawIndex), 'utf8');
    })
    .catch(function (err) {
      console.log("Failed:", err);
    });
};

Thanks in advance.


Solution

  • Using forEach wasn't the right choice to make as one wants to return a Promise. Thus it is wiser to make use of .map and then return Promises in the if/else statement. Finally, one has to call Promises.all(promises) making .then(...) usable as expected.

    My final function:

    function buildIndex(rawIndex, root, indexPath = root + 'js/app/index.json') {
      var path = path || require('path');
      var fs = fs || require('fs'),
        promises = [],
        ignore = ['node_modules'],
        files = fs.readdirSync(root);
    
      var promises = files.map(function (file) {
        if (fs.statSync(path.join(root, file)).isDirectory() && ignore.indexOf(file) == -1) {
          return buildIndex(rawIndex, path.join(root, file), indexPath);
        }
        else if (file.substr(-5) === '.html' && file != 'example.html') {
          return JSDOM.fromFile(path.join(root, file)).then(dom => {
    
            var $ = require('jquery')(dom.window);
            populate();
            console.log(file + " indexé");
    
            function populate() {
              $('h1, h2, h3, h4, h5, h6').each(function () {
                var title = $(this);
                var link = path.join(root, file).replace('..\\', '') + "#" + title.prop('id');
                var body = title.nextUntil('h1, h2, h3, h4, h5, h6');
                rawIndex.add({
                  id: link,
                  title: title.text().latinise(),
                  body: body.text().latinise()
                });
              });
            };
          })
        }
      })
      return Promise.all(promises).then(function () {
        fs.writeFileSync(indexPath, "var data = " + JSON.stringify(rawIndex), 'utf8');
      });
    };
    

    Thanks @Bergi for the answer and those who helped.