0

I am writing a SPA with typescript using breeze and knockout.

What I want to do is to create a launch manager, which can perform the neccessary steps required to even start the site (e.g. read configuration json, download odata metadata, initialize breeze metadata store and so on).

I've created the following to represent each step in the launch sequence:

export enum LauncherProgressStatus {
    Ready,
    InProgress,
    Success,
    Failed,
    Aborted
}

export class LauncherProgressItem {
    public status: KnockoutObservable<LauncherProgressStatus> = ko.observable<LauncherProgressStatus>();
    public description: KnockoutObservable<String> = ko.observable<String>();

    public statusText: KnockoutComputedFunctions<String> = ko.computed<String>(() => {
        return LauncherProgressItem.getStatusText(this.status());

    });

    public start() {
       this.action(this);
    }

    constructor(descriptionText: String,
                public action: (arg: LauncherProgressItem) => Boolean) {
        this.status(LauncherProgressStatus.InProgress);
        this.description(descriptionText);
    }

    public static getStatusText(status: LauncherProgressStatus) : String {
        switch (status) {
            case LauncherProgressStatus.Ready:
                return "Ready";
            case LauncherProgressStatus.InProgress:
                return "In progress";
            case LauncherProgressStatus.Success:
                return "Success";
            case LauncherProgressStatus.Aborted:
                return "Aborted";
            default: 
                return "Failed";
         }
    }
}

TL;DR I create each step like this in code:

var item1 = new launcher.LauncherProgressItem("Loading configuration...", (item: LauncherProgressItem) => {
    cfgMgr.setConfigurationFromFile("config.json?bust=" + (new Date()).getTime());
    return true;
});

Now the problem: I want to utilize this to create a promise chain using Q. I can do this manually, i.e.

q.fcall(() => item1.action(item1))
    .then(() => item2.action(item2))
    .fail((r) => { console.log("Many whelps, HANDLE IT!") });

But I want to create some kind of manager object that doesnt really know how many steps is required. It will just be responsible for building an array of promises and execute them in sequence, whilst being able to detect errors (in the fail promise presumably) and abort the sequence.

The manager will have some kind of collection containing the LauncherProgressItem steps. Then I'm looking to build a chain of promises based on the content of that collection.

I've been looking at this for a while now but can't really seem to get my head around how to do this with Q. I've seen some examples etc but I don't really understand how it works.

Anyone got any suggestions on how to achieve this?

Update: I'll try to clarify what I am trying to achieve: My LauncherProgressItem wraps a lambda function and some state information which I bind to my view. This is why I am using these, but this is kind of irrelevant to what I'm actually struggling with.

So lets assume I have a class which contains an array of lambdas. This class has a method which will run all these lambas in sequence using Q, aborting on error. Exactly what I would achieve with the following code:

Q.fcall(doSomething).then(doSomethingElse).fail(reportError);

However, in this case doSomething and doSomethingElseresides in an array of functions, rather than a fixed number of steps. This is because I want it to be reusable, i.e. being able to run in multiple scenarios depending on the task at hand. So I want to avoid hard-coding the chain of functions to run.

havardhu
  • 3,576
  • 2
  • 30
  • 42
  • 1
    Why do you need all these `LauncherProgressItem`s - what's wrong with just using promises for the order of operations? That's what promises do anyway.. – Benjamin Gruenbaum Sep 13 '14 at 12:44
  • You may find [this](http://taoofcode.net/promise-anti-patterns/) to be of interest. The article is actually about various "anti-pattern" sins, none of which you are committing (well, none that we know of :) ), but the second "good" pattern under the heading "The Collection Kerfuffle" may at least help you better understand the problem. Some light adaptation of the pattern may even offer a solution. – Roamer-1888 Sep 13 '14 at 13:43
  • 2
    Possible duplicate of [How to sequentially run promises with Q in Javascript?](http://stackoverflow.com/q/18386753/1048572) – Bergi Sep 13 '14 at 17:48
  • @BenjaminGruenbaum My question is how to chain promises dynamically. You ask me 'why not just use promises' although I've clearly stated 'I've seen some examples etc but I don't really understand how it works.' Also downvoting me because I don't visit here every day? And over the weekend, no less! Cheers. I'm wrapping the items of work in classes so that I can bind them to my splash screen. There are probably millions of ways to do this, but I'm asking for advice here. Why don't you try to help constructively? – havardhu Sep 16 '14 at 09:54
  • @havardhu I'm trying to help constructively, a downvote is a way to prompt you to work on your question. The easiest thing for me to do is not comment which would make your question automatically deleted in a few days. I'm sorry if it seems harsh but it's because I'm trying to help. Can you clarify what you're actually trying to perform? What works for me is reading this from the perspective of someone who doesn't know my code. – Benjamin Gruenbaum Sep 16 '14 at 10:10
  • @BenjaminGruenbaum Ok, I've updated my question to hopefully clarify what I want to do. If you still can't decipher it I'll just move on :) – havardhu Sep 16 '14 at 12:07
  • @havardhu oh, I think I understand the problem better now - let's say your array of functions is called `arr` and all the functions return promises. Does something like `var p = Q(); arr.forEach(function(el){ p = p.then(el); }); return p` work for you? – Benjamin Gruenbaum Sep 16 '14 at 12:17
  • Hmm I suppose. But how do I make my functions return a promise? This is the heart of the issue I think, the thing I can't get my head around :) – havardhu Sep 16 '14 at 12:23

1 Answers1

2

Sorry I don't know typescript but I thought the comment thread above was not going very well, so here's the function you asked for in plain JS:

function runInSequence (functions) {
    if (!functions || !functions.length) {
        return;
    }

    var nextPromise = Q.fcall(functions[0]);

    functions.slice(1).forEach(function (f) {
        nextPromise = nextPromise.then(f);
    });

    nextPromise.fail(yourErrorHandler);
}
Milimetric
  • 13,411
  • 4
  • 44
  • 56