Skip to content

Commit 44b391b

Browse files
committed
docs(async-transform): Added docs
1 parent 1c41e1c commit 44b391b

File tree

2 files changed

+200
-31
lines changed

2 files changed

+200
-31
lines changed

readme.md

Lines changed: 183 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -2,71 +2,224 @@
22

33
[![Build Status](https://travis-ci.org/BigAB/async-transform.png?branch=master)](https://travis-ci.org/BigAB/async-transform)
44

5-
A function for easy asynchronous flow
5+
A tiny utility function for composing asynchronous transformations
66

7-
## Usage
7+
- great for unit testing
8+
- [composable](#composition)
9+
- works great with ES2017's `async/await`
810

9-
### ES6 use
11+
## Install
1012

11-
With StealJS, you can import this module directly in a template that is autorendered:
13+
### ES6
14+
15+
With [StealJS](http://stealjs.com/), you can import this module with ES6 imports:
1216

1317
```js
1418
import plugin from 'async-transform';
1519
```
1620

17-
### CommonJS use
18-
19-
Use `require` to load `async-transform` and everything else
20-
needed to create a template that uses `async-transform`:
21+
### CommonJS
2122

2223
```js
2324
var plugin = require("async-transform");
2425
```
2526

26-
## AMD use
27+
### Standalone
2728

28-
Configure the `can` and `jquery` paths and the `async-transform` package:
29+
Load the `global` version of the plugin:
2930

3031
```html
31-
<script src="require.js"></script>
32+
<script src='./node_modules/async-transform/dist/global/async-transform.js'></script>
3233
<script>
33-
require.config({
34-
paths: {
35-
"jquery": "node_modules/jquery/dist/jquery",
36-
"can": "node_modules/canjs/dist/amd/can"
37-
},
38-
packages: [{
39-
name: 'async-transform',
40-
location: 'node_modules/async-transform/dist/amd',
41-
main: 'lib/async-transform'
42-
}]
43-
});
44-
require(["main-amd"], function(){});
34+
asyncTransform([transformFunctions], val);
4535
</script>
4636
```
4737

48-
### Standalone use
38+
## Use
4939

50-
Load the `global` version of the plugin:
40+
Async-Transform is a simple utility function, that takes an array of **transform functions** and a **value** and runs each transform function in sequence, passing the result of the last transform function (or value at the start) as the sole argument. `asyncTransform` returns a promise, which makes chaining and composing transform functions trivial.
5141

52-
```html
53-
<script src='./node_modules/async-transform/dist/global/async-transform.js'></script>
42+
A **transform function** is just a function that takes a single argument as the value and returns either a new value, or a promise that will resolve to a new value.
43+
44+
#### Basic use
45+
46+
```javascript
47+
// some are sync transforms, some are async
48+
const transformFunctions = [
49+
v => v+1,
50+
v => Promise.resolve(v*2),
51+
v => v*v,
52+
v => Promise.resolve({foo:v})
53+
];
54+
55+
asyncTransform(funcs, 1)
56+
.then( v => console.log(v) ); // { foo: 16 }
5457
```
5558

59+
#### Partial application
60+
61+
You can also omit the `value` argument and `asyncTransform` will return a **transform function** that will return a promise:
62+
63+
```javascript
64+
const transformFunctions = [
65+
v => v+1,
66+
v => Promise.resolve(v*2),
67+
v => v*v,
68+
v => Promise.resolve({ value: v })
69+
];
70+
71+
const process = asyncTransform( transformFunctions );
72+
73+
process(3)
74+
.then( v => console.log(v) ) // { value: 64 }
75+
```
76+
Since the partial application option returns what is considered a **transform function**, you can then use that return value to compose more complicated async-transformations, built up from easily testable pieces.
77+
78+
#### Setting context
79+
80+
You can also add an optional 3rd argument, which is the *[context](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Operators/this)* the transformFunctions will be run with (using `Function.prototype.call`).
81+
82+
```javascript
83+
class Thing {
84+
constructor(baseValue) {
85+
this.value = baseValue;
86+
}
87+
addValue(v) {
88+
return v + this.value;
89+
}
90+
multiplyValue(v) {
91+
return v * this.value;
92+
}
93+
wrap(v) {
94+
return { value: v };
95+
}
96+
calculate(v) {
97+
const transforms = [
98+
this.addValue,
99+
this.multiplyValue,
100+
this.wrap
101+
];
102+
return asyncTransform(transforms, v, this);
103+
}
104+
};
105+
106+
const process = asyncTransform( funcs );
107+
108+
process(3)
109+
.then( v => console.log(v) ) // { value: 64 }
110+
```
111+
112+
If you want to use the partial application option while also setting a context, just make sure to pass `undefined` for your value.
113+
114+
```javascript
115+
/*...*/
116+
buildDocumentationSite( filesGlob ) {
117+
const convertFilesToDocsJSON = asyncTransform([
118+
readFiles,
119+
parseFiles
120+
], undefined, this.plugins);
121+
122+
const docsToSiteData = asyncTransform([
123+
createSiteStructure,
124+
orderPages,
125+
orderMenu
126+
], undefined, this.plugins);
127+
128+
const generateDocsFromTemplates = asyncTransform([
129+
generateHTMLSite,
130+
generatePDF,
131+
generateMarkdownDocs
132+
], undefined, this.templates);
133+
134+
return asyncTransform([
135+
convertFilesToDocsJSON,
136+
docsToSiteData,
137+
generateDocsFromTemplates
138+
], filesGlob);
139+
}
140+
/*...*/
141+
```
142+
143+
Though ES2017's `async/await` feature may make hand-rolling your own async transformations super easy, `asyncTransform` still has a place due to it's context binding and dynamic composability, but it still works well **with** `async/await`.
144+
145+
```javascript
146+
async function get( req ) {
147+
const requestsToServer = await asyncTransform([
148+
checkBootstrapCache,
149+
checkLocalStore
150+
], req);
151+
const res = await axios.get( requestsToServer );
152+
const model = await asyncTransform([
153+
parseResponse,
154+
extractData,
155+
instantiateModel
156+
], res);
157+
addToLocalStore( model );
158+
return model;
159+
}
160+
```
161+
162+
#### Composition
163+
164+
Because the partial application option of `asyncTransform` returns a **transform function**, composition becomes trivial, and allows you to break down the problem into tiny, easily testable, bite-sized pieces and compose those pieces into a powerful asynchronous transformation function.
165+
166+
```javascript
167+
const hooks = {
168+
/*...*/
169+
findAll: {
170+
before: [
171+
authenticate({ field: 'user' }),
172+
authorize,
173+
convertToQueryParams,
174+
transformFields({ 'id': '_id' })
175+
],
176+
after: [
177+
payload => payload.data,
178+
transformFields({ '_id': 'id' })
179+
]
180+
}
181+
/*...*/
182+
}
183+
184+
const beforeFindAllHooks = asyncTransform(hooks.findAll.before);
185+
186+
const afterFindAllHooks = asyncTransform(hooks.findAll.after);
187+
188+
export const findAll = asyncTransform([
189+
beforeFindAllHooks,
190+
service.findAll,
191+
afterFindAllHooks
192+
]);
193+
194+
/* use:
195+
findAll({ completed: true })
196+
.then( completedTasks => display( completedTasks ) )
197+
.catch( err => displayError( err.reason ) )
198+
*/
199+
```
200+
201+
The example above, describes a complex findAll operation broken down into easy to test functions that do one thing well: `authenticate`, `authorize`, `convertToQueryParams`, and `transformFields`. By abstracting that complexity away, you can easily understand what `findAll` does without having to understand each piece until the point you need to.
202+
203+
**Async-Transform** is not a lot of code, it probably doesn't need to be it's own package, you could just copy the source into your own project. It is really more of a pattern. By unifying an interface: `a function that takes on value and returns a new value or promise of a value` we allow for composition and easy testing, and it's surprising how many problems can be solved using this pattern.
204+
205+
---
206+
207+
Though **async-transform** is pretty powerful and flexible, it still can only return one asynchronous value; What if you wanted to return more than one value, over time? When you want to take it to the next level, checkout [RxJS](https://github.com/Reactive-Extensions/RxJS) and other [FRP](https://github.com/stoeffel/awesome-frp-js) libraries in JavaScript. Good luck, and good learning!
208+
56209
## Contributing
210+
[Contributing](./CONTRIBUTING.md)
57211

58212
### Making a Build
59213

60214
To make a build of the distributables into `dist/` in the cloned repository run
61215

62216
```
63-
npm install
64-
node build
217+
npm run build
65218
```
66219

67220
### Running the tests
68221

69-
Tests can run in the browser by opening a webserver and visiting the `test.html` page.
222+
Tests can run in the browser by opening a webserver in the root, or running `npm run develop` and visiting the `/src/test` page.
70223
Automated tests that run the tests from the command line in Firefox can be run with
71224

72225
```

src/async-transform.md

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,20 @@
22

33
# async-transform
44

5-
A function for easy asynchronous transformation flow
5+
@module {function} async-transform
6+
@release 1.0
7+
@package ../../package.json
8+
9+
@description A function for easy asynchronous transformation flow
10+
11+
@signature `asyncTransform(transformFunctions:array(Function)[, value:any, context:any])`
12+
13+
**Transform Functions** are just functions that follow this pattern:
14+
15+
- They accept 1 argument (the value)
16+
- they return either a value or a promise that resolves to a value
17+
- they **do not** return `undefined` as the value, and should guard against so
18+
19+
That's it.
20+
21+
`asyncTransform` takes an `Array` of **Transform Functions** as it's first and only required argument.

0 commit comments

Comments
 (0)