Home

Awesome

<p align="center"> <a href="https://github.com/ibmruntimes/yieldable-json"> <img height="257" width="114" src="https://user-images.githubusercontent.com/6447530/32721130-891d044a-c88b-11e7-9a6d-db062b55169d.png"> </a> <p align="center">Asynchronous JSON.parse and JSON.stringify APIs</p> </p>

yieldable-json

This library provides asynchronous version of standard JSON.parse and JSON.stringify APIs.

Use Case

Node.js based web applications dealing with large JSON objects while requiring high level of concurrency for the transactions.

Here 'largeness' applies to any of, or all of, or any combination thereof deep compositions, large number of fields, and fields with large data (such as massive strings).

In contrast to JSON Streams, which are built with custom stream oriented protocols (at library as well as application level) and used in special use cases like data analytics, monitors, etc., yieldable-json works on fully formed JSON data that is characterized only by object size and designed with an objective of improving concurrency in the application and thereby usable in any workload scenarios.


Statistics

Testing with a wide range of data showed that objects which are bigger than 256KB (when stringified) degrades concurrency in the application.

While definition of concurrency is subjective, a number of tests we performed in short and long round-trip networks showed that being available in the event loop for attending to concurrent requests at every 5 milliseconds provides the required responsiveness to clients, as well as meets the overall concurrency expectation on the application.

<table> <tr> <th rowspan="2">Data Volume</th> <th colspan="2">Loop starvation (in milliseconds)</th> </tr> <tr> <td>JSON (built-in)</td> <td>yieldable-json</td> </tr> <tr> <td>115 KB</td> <td>2</td> <td>5</td> </tr> <tr> <td>327 KB</td> <td>10</td> <td>5</td> </tr> <tr> <td>1.3 MB</td> <td>50</td> <td>5</td> </tr> <tr> <td>2.2 MB</td> <td>100</td> <td>5</td> </tr> </table>

As shown in the table, the yieldable-json guarantees the event loop is reached and processed in every 5 ms, irrespective of the JSON volume being processed.


Background

In Cloud based deployments, we foresee increase of JSON payload across distributed computing end-points in an exponential manner, causing performance bottleneck in the single-threaded node.js platform, and hence this attempt. The key function of asynchronous JSON APIs are to perform marshalling and un-marshalling of massive data incrementally, and yielding back to the application occasionally, and reduce loop starvation.

The ES6's generator function along with yield semantics is used for implementing incremental traversal. The extent of traversal in one iteration is controlled by the intensity parameter. While there are some globally accessible variables within main APIs, predominently the parser and stringifier is implemented through a self-recursing loop. The callback is guaranteed to fire no earlier than next tick to emulate the asynchronous behavior.

Because of the usage of ES6 semantics, this module is supported only on node v4.x and above.


APIs

stringifyAsync:

stringifyAsync(value[, replacer][, space][, intensity], callback)

  1. If you return a String, that string is used as the property's value when adding it to the JSON string.
  2. If you return a Boolean, "true" or "false" is used as the property's value, as appropriate, when adding it to the JSON string.
  3. If you return any other object, the object is recursively stringified into the JSON string, calling the replacer function on each property, unless the object is a function, in which case nothing is added to the JSON string.
  4. If you return undefined, the property is not included (i.e., filtered out) in the output JSON string.
const yj = require('yieldable-json')
yj.stringifyAsync({key:"value"}, (err, data) => {
  if (!err)
    console.log(data)
})

Warning: While stringifyAsync is in progress (i.e. before the callback is executed), it is the user's responsibility to ensure that the Javascript object value (or any of its child objects) is not modified in any way. Object modification between the async function invocation and issuing of the completion callback may lead to undefined behavior (and can possibly result in an inadvertent crash or object corruption).

parseAsync:

parseAsync(text[, reviver][, intensity], callback)

const yj = require('yieldable-json')
yj.parseAsync('{"key":"value"}', (err, data) => {
  if (!err)
    console.log(data)
})

GitHub Issues

This project uses GitHub Issues to track ongoing development and issues. Be sure to search for existing bugs before you create another one. Contributions are always welcome!

Collaborators