Skip to content

Commit

Permalink
Update README.md
Browse files Browse the repository at this point in the history
  • Loading branch information
abumq committed Jul 23, 2023
1 parent 4125a2d commit 1f1d149
Showing 1 changed file with 50 additions and 182 deletions.
232 changes: 50 additions & 182 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
<a href="https://github.com/abumq/airsync">
<img width="190px" src="https://github.com/abumq/airsync/raw/main/assets/logo.png?" />
</a>
<p align="center">Empower asyncronous calls</p>
</p>

<p align="center">
Expand Down Expand Up @@ -45,236 +44,103 @@ const airsync = require("airsync");
# Introduction
AirSync is a powerful javascript library that you can use when using `async`.

Consider this function:
The easiest way to see the power of this library is to checkout [`performance.js`](https://github.com/abumq/airsync/blob/main/examples/performance.js) file.

AirSync helps you:

1. When you create a JSON, you are guaranteed to have all the fields resolved instead of pending promises.
2. When you access function parameter, you are guaranteed to have all the parameters resolved instead of pending promises.
* Create JSON from promises without extracting functions or multiple await
* Convert your existing functions that take promises as parameters, you do not need to wait for promises to fulfil in order to pass them to the function. You can just pass them in as is.

## 1. JSON

### TL;DR

What do you think output would be?
The best part is that AirSync makes your code readable while using full power of [non-blocking event based I/O](https://developers.redhat.com/blog/2016/08/16/why-should-i-use-node-js-the-non-blocking-event-io-framework/) that Node.js is known for.

# 1. JSON
## Problem
```javascript
const calcAge = async () => 65; // note the async

(async () => {
console.log({
age: calcAge(),
});
})();
// this will not result in correct `age`
const props = {
age: calcAge(),
}

// output:
// {age: Promise}
// Promise {<fulfilled>: undefined}
```

How do we solve this problem? Either by adding a lot of awaits for each promise, or just by simply:

```javascript
const { json } = require("airsync");

const calcAge = async () => 65;

(async () => {
console.log(
await json({
// notice using airsync' json() function
age: calcAge(),
})
);
})();

// output:
// {age: 65}
```

Try it on [RunKit](https://npm.runkit.com/airsync)

<details>
<summary>Read in detail</summary>

### The Problem

Let's say you need to create a JSON

```javascript
{
id: 1,
name: 'John F. Kennedy',
age: 45,
}
```

This is good as long as you are not using promises, but if you want to use promises like:

## Existing Solution
Either you can write await for each one of the promises, like:
```javascript
const queryName = () => Promise.resolve('John F. Kennedy');
const calculateAge = () => Promise.resolve(45);

{
id: 1,
name: queryName(),
age: calculateAge(),
const props = {
age: await calcAge(),
}
```

this will result potentially unresolved promises.
### Problems with this solution

and if you do this:
* This is going to blow out very soon, meaning, as you introduce new calls or fields in this JSON, it will become very unreadable
* The biggest problem with this is performance. This is becoming blocking calls as we are going to await for the results from functions

```javascript
{
id: 1,
name: await queryName(),
age: await calculateAge(),
}
```
that will blow out very soon as you scale up.

the calls are now sequential and defeats the purpose of [non-blocking event based I/O](https://developers.redhat.com/blog/2016/08/16/why-should-i-use-node-js-the-non-blocking-event-io-framework/) that Node.js is known for.

### Solution

To handle this situation without wrapping the promise resolution in a separate function, you can use this utility package to handle this situation
## AirSync Solution

You can use AirSync to solve this issue
```javascript
const { json } = require("airsync");

await json({
id: 1,
name: queryName(),
age: calculateAge(),
});
const props = json({
age: calcAge(),
})
```

This will resolve only after all the promises are resolved. Resulting in:

```javascript
{
id: 1,
name: 'John F Kennedy',
age: 45,
}
```

</details>
Try it on [RunKit](https://npm.runkit.com/airsync)

### Max depth
## Max depth

Default object depth supported by airsync is `64`.

## 2. Async Function

### TL;DR

What do you think output would be?

# 2. Async Function
## Problem
```javascript
const getAge = async () => 123;
const getDetail = async (age) => `The age is ${age}`;
```

(async () => {
const result = await getDetail(getAge());
console.log(result);
})();
```javascript
await getDetail(getAge());

// output:
// The age is [object Promise]
// Promise {<fulfilled>: undefined}
```

How do we solve this problem?

## Existing Solution
Either you can wait for the `getAge()` to be resolved first and then pass it to `getDetail()` like this:
```javascript
const { fn } = require("airsync"); // or you can import using const airsync = require('airsync') and use airsync.fn(...)

const getAge = async () => 123;
const getDetail = fn(async (age) => `The age is ${age}`); // notice the wrap around "fn"

(async () => {
const result = await getDetail(getAge());
console.log(result);
})();
await getDetail(await getAge());
```

Try it on [RunKit](https://npm.runkit.com/airsync)
or you can extract out it to variable.

<details>
<summary>Read in detail</summary>
### Problems with this solution

### Problem
* This is going to blow out very soon, meaning, as you introduce new calls, it will become very unreadable
* The biggest problem with this is performance. This is becoming blocking calls as we are going to await for the results from functions

There are times when you want to use promise values without awaiting (i.e, automatically once the promise is fulfilled). This utility helps you achieve this goal using native promise mechanism.

We will walk you through an example and provide explanation where necessary.

Let's say you have various utility functions to query the database.
## AirSync Solution

```javascript
const queryCompanyInfo_ = async () => ({
username: "@amrayn",
});

const queryAccountInfo_ = async (company) => ({
company,
created: "19-02-2020",
});
```

Notice the `queryAccountInfo_` takes `company` parameter (promise) that will be provided by `queryCompanyInfo_`. But you don't know whether this promise is fulfilled or not. If you use `Promise.all` directly (without this library) you won't be able to provide this (resolved) company object to `queryAccountInfo_`.

```javascript
Promise.all([
queryCompanyInfo_(),
queryAccountInfo_(), // notice we cannot provide "company" here
]).then(([userInfo, accountInfo]) => {
console.log(accountInfo);
});
```

A possible solution is to await for the promises first:

```javascript
const companyInfo = await queryCompanyInfo_();
const accountInfo = queryAccountInfo_(companyInfo);
```

This has 2 basic problems.

1. You're not making use of parallelism here. Which defeats the purpose of Promises to some extent.
2. The code is very soon going to be messy and unreadable.

### Solution

airsync allows you to "craft" a function that will help you achieve your goal without worrying about any of the above problem.

```javascript
const { fn } = require("airsync"); // or you can import { fn }

const queryCompanyInfo = fn(queryCompanyInfo_);
const queryAccountInfo = fn(queryAccountInfo_);

const finalJson = await queryAccountInfo(queryCompanyInfo());
```
const { fn } = require("airsync"); // or you can import using const airsync = require('airsync') and use airsync.fn(...)

This will result in:
const getAge = async () => 123;
const getDetail = fn(async (age) => `The age is ${age}`); // notice the wrap around "fn"

```javascript
{
company: {
username: '@amrayn',
},
created: '19-02-2020',
}
const result = await getDetail(getAge());
```

</details>

## Misc
Try it on [RunKit](https://npm.runkit.com/airsync)

### Convert param based function
## Function with JSON
`fn()` is good when the function takes normal parameters, e.g, `myfn(param1, param2)` but this will not work if we have json based parameters, e.g, `myfn({ param1: 1, param2: Promise.resolve(2) })`

For this `fnjson()` was added.
Expand All @@ -289,7 +155,9 @@ const getNameSync = fnjson(getName);

Now if you pass in JSON with unresolved promises to the function, it would be correctly passed in to `getName` function

### Get Object Value
# Misc Features

## Get Object Value

If you have a function that returns an object, and you want to grab just one specific value from the object, you can use built-in `get` function to do that.

Expand All @@ -315,7 +183,7 @@ Synopsis: `get(object, path, defaultValue, options)`. The `options` is passed th

**NOTE:** This function uses [lodash.get](https://www.npmjs.com/package/lodash.get) to resolve the JSON path.

### Options
## Options

If the first parameter is an object for the `fn()`, that object is used for setting up the options.

Expand Down Expand Up @@ -353,7 +221,7 @@ Following are the possible options
| `endTime` | Function for [server timing](https://www.w3.org/TR/server-timing/) - `(name) => {}` - the `name` is passed back to this function |
| `debug` | Boolean value to tell airsync whether debug logging is enabled or not. It will use a global `logger.debug()` object. If no such object exists, it will use `console.debug()` |

### Bulk Export (Advanced)
## Bulk Export (Advanced)

Converting existing exports to crafted functions is easy, either using `fn` for each function which can be cumbersome depending on number of functions; or you can simply convert the whole object using a helper function `fnExport`.

Expand Down

0 comments on commit 1f1d149

Please sign in to comment.