3

I am attempting to create a library to make API calls to a web application (jira, if you care to know) I have my api calls working no problem, but I am looking to make the code a bit more readable and use-able. I have tried searching for my needs, but it turns out I am not sure what I need to be searching for.

I am having an issue with Asynchronous calls that depend on each other, I understand that I have to wait until the callback is ran to run my next item, but I am not sure of the best way to design this.

I really would like to make Chaining a feature of my api, which I would hope to look like this:

createProject(jsonProjectStuff)
  .setLeadUser("myusername")
  .createBoard("boardName")
     .setBoardPermissions(boardPermissionJSONvar)
  .addRole(personRoleJSONvar);

with this example, everything would have to wait on the createProject as it will return the project. createBoard doesn't rely on the project normally, but used in this context it should be "assigned" to the project made, setting the board permissions only relies on the createBoard to work. addRole is specific to the project again.

the questions I have are:

  1. Is this possible to switch context like this and keep data in-between them without the need to run the function from the response hard coded?
  2. If this is possible, is this a good idea? If not I am open to other schemes.

I can think of a couple ways to make it work, including registering the function calls with a dependency tree and then fulfilling promises as we go, although that is mostly conceptual for me at this point as I am trying to decide the best.

Edit 2/19/2016

So I have looked into this more and I have decided on a selective "then" only when it creating a new item doesn't relate directly to the parent.

//Numbers are ID, string is Name
copyProject(IDorName)
  .setRoles(JSONItem)
  .setOwner("Project.Owner")
  .setDefaultEmail("noreply@fake.com")
  .then(
     copyBoard(IDorName)
       .setName("Blah blah Name {project.key}"),

     saveFilterAs(IDorName, "Board {project.key}", 
                  "project = {project.key} ORDER BY Rank ASC")
       .setFilterPermissions({shareValuesJSON})
   )

I like this solution a lot, the only thing I am unsure of how to do is the string "variables", I suppose it could be "Blah blah Name " + this.project.key either way I am unsure of how to give copyBoard or saveFilterAs access to it via the "then" function.

Any thoughts?

Krum110487
  • 611
  • 1
  • 7
  • 20
  • 1
    It appears a good idea... You can store the last promise, and for each function called, you can to chain to the last promise, and updates the last promise variable. – Joao Polo Feb 03 '16 at 16:32
  • @JoaozitoPolo how would this work with the context of calling a function 4 deep that needs the variables from the first call, would you mind showing a simple example of this so I can modify and use it to my needs? – Krum110487 Feb 03 '16 at 16:38
  • 2
    related: [Combining promises and chaining](http://stackoverflow.com/q/26837334/1048572). In essence, javascript is flexible enough to support any asynchronous fluent pattern you'd imagine, but they are not necessarily performant or simple to implement. – Bergi Feb 03 '16 at 16:55
  • 1
    We´re still using an event system for that but I´m looking forward to Reactive-Programming check https://github.com/ReactiveX/rxandroid/wiki where you can observe and subscribe – Christian Stengel Feb 03 '16 at 17:21
  • @Bergi after reading your post, do you not recommend promises due to the fact of having to resolve the context in which the promise is running from is too much of a hassle? I think I need to play around with promises more to understand why, and how then() or call() help alleviate that. – Krum110487 Feb 03 '16 at 18:31
  • 2
    @Krum110487: The context is not so much a problem given arrow functions. In general I think it's a bad idea to have instances whose state is tied to promises, rather make promises for instances with well-defined state (see also [here](http://stackoverflow.com/a/24686979/1048572)). Of course that's detrimental for method chaining on the instance; still, if you insist on a fluent interface you should not let this principle go - rather build a second class around the promise for the instance and give it the chainable methods. – Bergi Feb 03 '16 at 18:43

1 Answers1

-1

I've been using Nightmare (a headless browser) lately.

It has a fluent API that uses a nice design pattern.

Calling the API doesn't directly execute the actions, it only queues them and when you are ready to execute you must call the end function which returns a promise. The promise is resolved when the queue has completed its async execution.

For example, in your situation

createProject(jsonProjectStuff)
    .setLeadUser("myusername")
    .createBoard("boardName")
    .setBoardPermissions(boardPermissionJSONvar)
    .addRole(personRoleJSONvar)
    .end() // Execute the queue of operations.
    .then() => {
        // All operations completed.
    ))
    .catch(err => {
        // An error occurred.
    });

I feel like this pattern is quite elegant. It allows you to have a fluent API to build a sequence of actions. Then when you are ready to execute said operations you call end (or whatever). The sequence of operations are then completed asynchronously and you use the promise to handle completion and errors.

Ashley Davis
  • 9,896
  • 7
  • 69
  • 87