Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to call Q promise notify within the promise chain

I need helps on notify() within the promise chain.

I have 3 promise base functions connect(), send(cmd), disconnect(). Now I would like to write another function to wrap those call in following manner with progress notification.

function bombard() {
 return connect()
  .then(function () {
    var cmds = [/*many commands in string*/];
    var promises = _.map(cmds, function (cmd) {
     var deferred = Q.defer();
     deferred.notify(cmd);
     send(cmd).then(function (result) {
      deferred.resovle(result);
     });
     return deferred.promise;
    });
    return Q.all(promises);
  })
 .finally(function () { return disconnect() })
}

Run the function like that

bombard.then(onResolve, onReject, function (obj) {
 console.log(ob);
});

I supposed I will get notification for every command I have sent. However, it does not work as I expected. I get nothing actually.

Although I believe this is due to those notifications havn't propagated to outside promise, I have no idea on how to propagated those notifications on Q or wrapping that promise chain: connect, send, disconnect in a one deferred object.

Thanks

like image 471
Mond Wan Avatar asked Dec 14 '22 22:12

Mond Wan


1 Answers

I have some good news and some bad news.

Very good! You have found out the problem with the notifications API and why it is being removed in Q in the v2 branch, being deprecated in newer libraries like Bluebird, and never included in ECMAScript 6. It really boils down to the fact promises are not event emitters.

The notifications API does not compose or aggregate very well. In fact, notifications being on promises does not make too much sense imo to begin with,.

Instead, I suggest using a progress notification even, kind of like IProgress in C#. I'm going to simulate all the actions with Q.delay() for isolation, your code will obviously make real calls

function connect(iProgress){
    return Q.delay(1000).then(function(res){
        iProgress(0.5,"Connecting to Database");
    }).delay(1000).then(function(res){
        iProgress(0.5,"Done Connecting");
    });

} 

function send(data,iProgress){
     return Q.delay(200*Math.random() + 200).then(function(res){
         iProgress(0.33, "Sent First Element");
     }).delay(200*Math.random() + 400).then(function(){
         iProgress(0.33, "Sent second Element");
     }).delay(200*Math.random() + 500).then(function(){
         iProgress(0.33, "Done sending!");
     });
}
// disconnect is similar

Now, we can easily decide how our promises compose, for example:

function aggregateProgress(num){
     var total = 0;
     return function(progression,message){
          total += progression;
          console.log("Progressed ", ((total/num)*100).toFixed(2)+"%" );
          console.log("Got message",message);
     }
}

Which would let you do:

// bombard can accept iProgress itself if it needs to propagate it
function bombard() {
 var notify = aggregateProgress(cmds.length+1);
 return connect(notify)
  .then(function () {
    var cmds = [/*many commands in string*/];
    return Q.all(cmds.map(function(command){ return send(command,notify); }));
  });
}

Here is a complete and working fiddle to play with

like image 88
Benjamin Gruenbaum Avatar answered Dec 17 '22 11:12

Benjamin Gruenbaum