Task Management

The following use case was copied from the blog of Parse, a Facebook company. A quote:

Earlier this year we introduced the Bolts Framework, a set of low-level libraries to make developing mobile apps easier and faster. One of the core components of Bolts is Tasks. Tasks brings the programming model of JavaScript Promises to iOS and Android.

Today, we are proud to open an entirely new set of public APIs with the new versions of our iOSOS X and Android SDKs. Inside this release you will find that every method that was once intended to be asynchronous now has a Tasks-based counterpart. Organizing complex asynchronous code is now much more manageable in addition to being easier and faster to implement.

For example, here’s how you can find posts, mark them all as published, and update the UI on iOS:

Before giving Parse’s code, here is the SubScript solution:

val query = ParseQuery.getQuery("Post")
query.whereNotEqualTo("published", true)

query.findInBackground ~~(task:Task[List[ParseObject]])~~> 

for (post <- task.getResult)
& (  post.put("published", true)
     post.saveInBackground
  )

@Gui: mPostStatusTextView.setText(String.format("All Posts Published!"))

findInBackground and saveInBackground are futures; an implicit conversion script converts such a future into code that starts it and listens to its completion.
The arrow is a pipeline operator, comparable with “|” in Unix shell language. A major difference is that inside this arrow the transferred data gets a name and a type. Here the type could have been inferred, so that ~~(task)~~> would also be sufficient.
Inside the for loop there is an ampersand as operator. This requires all saveInBackground to complete in order to complete itself.
Finally a message is published on the screen; the @Gui: annotation ensures this is done in the Gui thread.

Now compare that with Parse’s own version for Android:

ParseQuery<ParseObject> query = ParseQuery.getQuery("Post");
query.whereNotEqualTo("published", true);
query.findInBackground().onSuccessTask(new Continuation<List<ParseObject>,Task<Void>>() {
  @Override
  public Task<Void> then(Task<List<ParseObject>> task) throws Exception {
    List<ParseObject> results = task.getResult();

    List<Task<Void>> saveTasks = new ArrayList<Task<Void>>();
    for (final ParseObject post : results) {
      post.put("published", true);
      saveTasks.add(post.saveInBackground());
    }
    return Task.whenAll(saveTasks);
  }
}).onSuccess(new Continuation<Void, Void>() {
  @Override
  public Void then(final Task<Void> task) {
    mPostStatusTextView.setText(String.format("All Posts Published!"));
    return null;
  }
}, Task.UI_THREAD_EXECUTOR);

The difference in boilerplate code is partly due to using Scala vs Java, and partly (mostly?) to using SubScript in the first version.
Note that in both versions error handling is lacking. In SubScript we plan to support error handling data flow using broken arrows: ~/~>, but there is still work to be done there.

Leave a Reply