1

I have the following javascript code written to run in node.js:

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

var copy = function(paramSource, paramDestination, paramCallback) {
  if (fs.existsSync(paramDestination)) {
    return paramCallback("Destination Exists"); //TODO make formal error code
  } else {
    fs.lstat(paramSource, function(error, sourceStats) {
      if (error) {
        return paramCallback(error);
      } else {
        if (sourceStats.isDirectory()) {
          fs.readdir(paramSource, function(error, sourceContents) {
            if (error) {
              return paramCallback(error);
            } else {
              fs.mkdir(paramDestination, sourceStats.mode, function(error) {
                if (error) {
                  paramCallback(error);
                } else {
                  if (sourceContents.length > 0) {
                    sourceContents.forEach(function(sourceChild) {
                      copy(path.join(paramSource, sourceChild), path.join(paramDestination, sourceChild), paramCallback);
                    });
                  }
                  return paramCallback(null, paramDestination);
                }
              });
            }
          });
        } else {
          var sourceFile = fs.createReadStream(paramSource);
          var destinationFile = fs.createWriteStream(paramDestination); //TODO permissions
          sourceFile.on("error", function(error) {
            return paramCallback(error);
          });
          destinationFile.on("error", function(error) {
            return paramCallback(error);
          });
          sourceFile.on("close", function() {
            //return paramCallback(null, paramSource);
          });
          destinationFile.on("close", function() {
            return paramCallback(null, paramDestination);
          });
          sourceFile.pipe(destinationFile);
        }
      }
    });
  }
};

var entities = 0;

copy("/home/fatalkeystroke/testing/directory_01", "/home/fatalkeystroke/testing/directory_01_copy", function(error, name) {
  if (error) {
    console.log(error);
  } else {
    console.log(name);
    entities++;
  }
});

console.log("Copied " + entities + " entities.");
console.log("Program Done.");

It's a testing exerpt from an application I'm working on. I would like to keep the asynchronous nature of the copy() function, however I also want a way to know when the full copy is complete (as the code related to entities implies). The reasoning is because I want to be able to tell when it's done of course, but also implement a progress tracking capability.

Is there a way to synchronously wait for the completion of a recursive asynchronous function in Javascript?

FatalKeystroke
  • 2,882
  • 7
  • 23
  • 35
  • 2
    No but async/await syntax can make your code appear as if you are. – pvg Jun 05 '17 at 03:52
  • 1
    What do you mean by "synchronously wait for the completion of an asynchronous function"? Is that not the purpose of `paramCallback`? – guest271314 Jun 05 '17 at 03:54
  • 3
    a moments thought and you'd realise how impossible that is :p – Jaromanda X Jun 05 '17 at 03:55
  • @guest271314 I want to be able to have the callback function passed to copy called with each file processed, but still know and be able to perform an action once all the files have been processed. – FatalKeystroke Jun 05 '17 at 03:57
  • Each of the function calls at `javascript` at Question has a callback function as parameter or capability to call a callback function, yes? Is `.on("close")` not dispatched at each close of destination file? What is purpose of `return` within an asynchronous callback function? – guest271314 Jun 05 '17 at 03:59
  • `return paramCallback(null, paramDestination);` is that not what this code is doing after the statement where you tested for the length of the files – 0.sh Jun 05 '17 at 04:01
  • See [Node.js: splitting stream content for n-parts](https://stackoverflow.com/questions/43631320/node-js-splitting-stream-content-for-n-parts/), [Chrome memory issue - File API + AngularJS](https://stackoverflow.com/questions/41440235/chrome-memory-issue-file-api-angularjs/41510379#41510379) – guest271314 Jun 05 '17 at 04:06
  • Yes, start using promises and wrap your code to work with promises instead of callbacks. – Tal Avissar Jun 05 '17 at 04:19

1 Answers1

-1

This could be done with sequential executor nsynjs. Your code will transform as follows:

var copy = function (src,dst) {
    if(nsynFs.lstat(nsynjsCtx, src).data.isDirectory()) {
        var files = nsynFs.readdir(nsynjsCtx, src).data;
        nsynFs.mkdir(nsynjsCtx,dst);
        for(var i=0; i<files.length; i++) {
            var f=files[i];
            console.log(f);
            copy(src+"/"+f, dst+"/"+f);
        }
    }
    else { // is File
        var input = nsynFs.open(nsynjsCtx,src,'r').data;
        var output = nsynFs.open(nsynjsCtx,dst,'w').data;
        var buf = Buffer.alloc(4096);
        while(true) {
            var readCnt = nsynFs.read(nsynjsCtx,input,buf,0,4096,null).data;
            if(!readCnt)
                break;
            nsynFs.write(nsynjsCtx,output,buf,0,readCnt,null);
        }
        nsynFs.close(nsynjsCtx,input);
        nsynFs.close(nsynjsCtx,output);
    }
};

copy("test1","test2");

Please see full working example here: https://github.com/amaksr/nsynjs/blob/master/examples/node-copy-files/index.js

amaksr
  • 7,555
  • 2
  • 16
  • 17