require JSON;

The $network.request action is used to dynamically fetch a single remote JSON file. But what if you want to fetch as many JSON files as you want, simultaneously?

Welcome to the world of $require.


parallel


Why would I want to do that?

Maybe you need to compose multiple sets of data to display to the view.

Maybe you need to fetch JSON objects from multiple different servers, each for a different purpose.

Or just maybe, you're building a completely decentralized networking app which needs to hit up every node individually!

Just remember that EVERYTHING on Jasonette (models, actions, views, templates, styles, or whatever) is expressed in JSON, which means you can dynamically require any part of your app.


In any case, this requires a powerful parallel networking feature.


Problem

Let's say we wanted to fetch a remote JSON object and assign it to a local variable:

{
  "type": "$network.request",
  "options": {
    "url": "https://jsonplaceholder.typicode.com/posts"
  },
  "success": {
    "type": "$set",
    "options": {
      "posts": "{{$jason}}"
    }
  }
}
  1. We use $network.request to fetch some posts as JSON.
  2. Then we assign the result to a local variable posts, using the $set action.


Now, what if we wanted to fetch from 6 different resources and do the same thing? Here's the code:

{
  "type": "$network.request",
  "options": {
    "url": "https://jsonplaceholder.typicode.com/posts"
  },
  "success": {
    "type": "$set",
    "options": {
      "posts": "{{$jason}}"
    },
    "success": {
      "type": "$network.request",
      "options": {
        "url": "https://jsonplaceholder.typicode.com/comments"
      },
      "success": {
        "type": "$set",
        "options": {
          "comments": "{{$jason}}"
        },
        "success": {
          "type": "$network.request",
          "options": {
            "url": "https://jsonplaceholder.typicode.com/albums"
          },
          "success": {
            "type": "$set",
            "options": {
              "albums": "{{$jason}}"
            },
            "success": {
              "type": "$network.request",
              "options": {
                "url": "https://jsonplaceholder.typicode.com/photos"
              },
              "success": {
                "type": "$set",
                "options": {
                  "photos": "{{$jason}}"
                },
                "success": {
                  "type": "$network.request",
                  "options": {
                    "url": "https://jsonplaceholder.typicode.com/todos"
                  },
                  "success": {
                    "type": "$set",
                    "options": {
                      "todos": "{{$jason}}"
                    },
                    "success": {
                      "type": "$network.request",
                      "options": {
                        "url": "https://jsonplaceholder.typicode.com/users"
                      },
                      "success": {
                        "type": "$set",
                        "options": {
                          "users": "{{$jason}}"
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}

There are two problems:

  1. The code is too long
  2. It will be 6 times slow because it runs these network requests one after one, serially.


Meet $require.

Now comes $require to the rescue:

{
  "type": "$require",
  "options": {
    "posts": "https://jsonplaceholder.typicode.com/posts",
    "comments": "https://jsonplaceholder.typicode.com/comments",
    "albums": "https://jsonplaceholder.typicode.com/albums",
    "photos": "https://jsonplaceholder.typicode.com/photos",
    "todos": "https://jsonplaceholder.typicode.com/todos",
    "users": "https://jsonplaceholder.typicode.com/users"
  },
  "success": {
    "type": "$set",
    "options": {
      "posts": "{{$jason.posts}}",
      "comments": "{{$jason.comments}}",
      "albums": "{{$jason.albums}}",
      "photos":  "{{$jason.photos}}",
      "todos": "{{$jason.todos}}",
      "users": "{{$jason.users}}"
    }
  }
}

No kidding, this is it! This code does exactly the same thing as above!

Furthermore it's 6 times faster since the requests are made concurrently.

Basically, $require waits until all of the requests have returned (either success or failure), and then calls its success callback with the returned JSON objects.

To reiterate:

  1. The code is much shorter
  2. Will be 6 times faster since the network requests are run in parallel


Syntax

Let's get into the actual syntax.

Just like any other actions, the $require action takes an options object. There are two patterns:

  1. Key: URL String
  2. Key: URL Array


1. Key: URL String

The same syntax we saw above. Basically, the $require action looks at all key: value pairs in the options object, fetches all the URL strings, and then assigns each result to its corresponding key of the return value.

{
  "type": "$require",
  "options": {
    "posts": "https://jsonplaceholder.typicode.com/posts",
    "comments": "https://jsonplaceholder.typicode.com/comments"
  },
  "success": {
    "type": "$set",
    "options": {
      "jasonplaceholder_posts": "{{$jason.posts}}",
      "jasonplaceholder_comments": "{{$jason.comments}}"
    }
  }
}

In this example, the keys are posts and comments, so you can access the results in the succeeding action ($set) using {{$jason.posts}} and {{$jason.comments}}.


2. Key: URL Array

You can also have an array of URLs (instead of a single URL string) as the value.

{
  "type": "$require",
  "options": {
    "result": ["https://jsonplaceholder.typicode.com/posts", "https://jsonplaceholder.typicode.com/comments"]
  },
  "success": {
    "type": "$set",
    "options": {
      "jasonplaceholder_posts": "{{$jason.result[0]}}",
      "jasonplaceholder_comments": "{{$jason.result[1]}}"
    }
  }
}

In this example, we only have a single key (result) and the value is an array of URLs. So when we fetch these remote JSON content, we push them into the result array. Once all JSON responses have returned, we go on to the next action $set, where we can access them using {{$jason.result[0]}} and {{$jason.result[1]}}.


Performance

The biggest concern I had when first working on this feature was performance. For some reason I had this idea that on mobile, only a single network request should be made at a time.

Turns out, this was absolutely a non-issue! Upon actually trying it out on my iPhone and Android, I found that there is no noticeable difference!

I thought about why this is so, and in retrospect it kind of makes sense. Here's the reasoning:

Think about a typical app where the view displays multiple items, each with an image and some text. When you load this view, it's going out there and concurrently fetching EVERY image and displaying it on the screen. Which means, this type of concurrent requests have existed on a low level, and have already been working fine without me realizing. And in most cases JSON files are significantly smaller than images. If concurrent image downloads work fine, concurrent JSON downloads should have no problem!


Note

$require has one job--fetch multiple JSON files in parallel--and it doesn't try to be more than that.

1. Only GET request

Since $require is more about requiring multiple JSON files and not about actually making requests that will update things on the server, every $require is actually a GET request under the hood.

2. Only JSON type

$require is strictly for fetching JSON.

If you want to fetch HTML, RSS, or any raw content, these are not meant to be done in parallel and you need to use $network.request.


Conclusion

Parallel network requests can be extremely powerful, especially considering how literally EVERYTHING on Jasonette is expressed in JSON.

This includes all of model, view, controller logic.

All you need to do is:

  1. Require some JSON object
  2. Assign them to a local variable
  3. Use it anywhere!

What's possible is only bound by your imagination.

I've been personally experimenting with $require and can see there's all kinds of potentially interesting use cases. If you find a clever use case, please share!