5

I am getting into programming with javascript and using Promises, right now using Q.js. I have finally gotten to a point where I understand what I am doing, but am having a difficult time with a specific behavior.

I have one situation where I have reasonably similar code repeated several times. It basically goes like this ...

{
   // start
   var deferred = Q.defer();

   // do something {
      deferred.resolve();
   }

   return deferred.promise;
}

Okay, that's all fine and good, but repeating all of this every time was getting annoying, so I attempted to wrap it up in something. This is just an example, it is not the entire javascript file, since most of the other parts are not relevant.

{
   var list = [];
   queue = function(f) {
      var deferred = Q.defer();
      list.push(f(deferred));
      return deferred.promise;
   }

   {
      queue(function(deferred){
         // do some work
         // we want the deferred here so we can resolve it at the correct time
         deferred.resolve();
      });
   }
}

The problem is that I don't want this to run the instant I queue it up. I basically want to build the list, and then run it later. I am running the list using the reduce function in Q.js

{
   return list.reduce(function(i, f) {
      return i.then(f);
   }, Q());
}

But this is kind of counter to my goal, since I really don't intend to run them at the same time they are queued. Is there a way to save the execution for later and still pass the deferred object through the function?

Update

I was asked what I expect the code to do, which is a fair question. I'll try to explain. The purpose of this is to split up the logic because I am using ASP.NET MVC, and so I have _Layout pages, and then normal views - so there is logic that cannot run until other things are completed, but some times that is on a per-page basis. This method was contrived to deal with that.

Essentially it works like this ...

Loader.js

This is, for lack of a better term or current implementation, a global object. I have plans to change that eventually, but one step at a time.

{
   var Loader = {};
   var list = [];

   initialize = function() {
      Q().then(step1).then(step2).then(process).then(finalStep);
   };

   queue = function(f) {
      // push the given function to the list
   };

   process = function() {
      return list.reduce(function(i,f){ 
         return i.then(f);
      }, Q());
   };

   step1 = function() { // generic example
      // create a promise
      return deferred.promise;
   }; // other steps are similar to this.

   return Loader;
}

_Layout

<head>
   @RenderSection("scripts", false)
   <script type="text/javascript">
      // we have the loader object already
      Loader.initialize();
   </script>
</head>

Index

@section Scripts {
   <script type="text/javascript">
      Loader.promise(function(deferred){
         // do something here.
         deferred.resolve();
      }));
   </script>
}
Ciel
  • 4,290
  • 8
  • 51
  • 110
  • I don't understand what your problem is. To [run promises in a list](http://stackoverflow.com/q/23650204/1048572), or to [promisify a functionality](http://stackoverflow.com/q/22519784/1048572) by passing it a deferred (like an "output parameter")? – Bergi Jul 08 '14 at 18:49
  • How do you want to start the execution, if not as soon as can? – Bergi Jul 08 '14 at 18:50
  • Those are really good questions, I have updated the question to illustrate the means and intent a bit better. Please let me know if I need to add more details. – Ciel Jul 08 '14 at 18:57
  • And where in that big picture are new tasks `queue()`d? You seem to want to `process` them after some initialisation `step1`/`step2`s, but the `list` is actually still empty? – Bergi Jul 08 '14 at 19:04
  • At this point in the process, the list is populated by simple merit of being first in line. The initialization doesn't happen until the DOM is ready. Eventually I will wire this to actual events for correct flow control, but as it is written right this very second, it is more of a race to get things into the queue. – Ciel Jul 08 '14 at 19:05
  • So when you call up `Index`, the Index's view is loaded, and its `Scripts` section runs. It is first, and it doesn't wait for the DOM, so it fills the `list`. Then when the DOM is ready, the `Loader.initialize()` function runs. This is not ideal and it is not how I plan to keep it, but it is as far as I've gotten. – Ciel Jul 08 '14 at 19:07
  • I see. So the `index` script will call `queue`, and everything is included after the `Loader.js`? – Bergi Jul 08 '14 at 19:09
  • At this point in time, yes. Essentially I need to run things in a specific order to make sure data is sent to the right places. Dividing that between inheriting view and layout was proving very difficult, so this is my second attempt. If this doesn't work, I'll take a different approach. – Ciel Jul 08 '14 at 19:10

2 Answers2

6

You could use a closure.

queue(function(deferred) {
    return function() {
        // this is the actual function that will be run,
        // but it will have access to the deferred variable
        deferred.resolve();
    };
});
dpk2442
  • 701
  • 3
  • 8
1

I think you should do something like

var Loader = {
    promise: function(construct) {
        var deferred = Q.defer();
        construct(deferred);
        return deferred.promise;
    },
    queue: function(f) {
        this.ready = this.ready.then(f);
    },
    ready: Q.Promise(function(resolve) {
        window.onload = resolve; // or whatever you need to do here
        // or assign the resolve function to Loader.initialize and call it later
    })
};

Then Loader.queue() functions that return other promises.

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • Wow, this is pretty cool too. This did successfully do the same thing. At the immediate moment, I'm not switching over to this model, but I will study it some more and see what I can do. I am a strong believer in always finding two ways to do anything that you don't understand, instead of just taking the first solution you find. Thank you very much! – Ciel Jul 08 '14 at 21:42