javascriptnode.jspromisenodegit

Return after all "on" events are called inside a JS promise


In Node.js, using NodeGit. I'm using a function similar to this inside a POST Express.js route. This route should get the commits between endCommit and startCommit:

router.post('/getLog', function(req, res, next) {
    var logData = [];

    Git.Repository.open(path.join(repositories.mine.path))
        .then(function(repo) {
            return {
                    endCommit: [ First commit object ], //Just a random commit object that has a "sha" inside
                    startCommit: [ Second commit object ]  //Just a random commit object that has a "sha" inside
                };
        })
        .then(function(range) {
            // History returns an event.

            var history = range.endCommit.history(Git.Revwalk.SORT.Time);

            // History emits "commit" event for each commit in the branch's history
            history.on("commit", function(commit) {
                logData.push({
                    commit: commit.sha(),
                    message: commit.message()
                });

                if (commit.sha() == range.startCommit.sha()) {
                    console.log("---LOG CREATED---");
                    history.end();
                }
            })

            history.start();
        })
        .done(function() {
            console.log("---RETURNING---");

            return res.json({ logData: logData });
        });
});

However, since history.on("commit", ...) is not a Promise the .done() function is called first. In the log I see:

---RETURNING---
---LOG CREATED---

How can I return only after the log has been created?

I've came across issues like this in the past however in this particular case I don't know how could I promisify the history object because it is based on events.


Solution

  • You can wrap event handling in a promise that should be resolved once you are done, and return it:

    .then(function(range) {
      // History returns an event.
    
      var history = range.endCommit.history(Git.Revwalk.SORT.Time);
    
      var commitPromise = new Promise(function(resolve, reject) {
        // History emits "commit" event for each commit in the branch's history
        history.on("commit", function(commit) {
          logData.push({
            commit: commit.sha(),
            message: commit.message()
          });
    
          if (commit.sha() == range.startCommit.sha()) {
            console.log("---LOG CREATED---");
            resolve(); // resolve the promise
            history.end();
          }
        })
      });
    
      history.start();
    
      return commitPromise;
    })
    

    I assume you have Promise global. It's up to you choosing particular promise implementation - you may want to use bluebird for example.