Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

modularize async utils #9

Merged
merged 3 commits into from
Mar 3, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 69 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,64 @@

utility methods to aid in writing [actions](http://fluxible.io/api/fluxible-context.html#executeaction-action-payload-callback-) for [fluxible](http://fluxible.io) based applications.

## Links
* [async.auto](https://github.com/caolan/async#autotasks-callback)
- used under the hood to actually handle task/action depedency management. (thanks to [@caolan](https://github.com/caolan))
## Modularized Builds/Requires
The utility library provides modularized methods, and method categories to aid in providing smaller [browserify](http://browserify.org/) and [webpack](http://webpack.github.io/) builds.

## Usage
### executeMultiple(context, actions, [done])
*available as of v0.1.0*
```js
var actionUtils = require('fluxible-action-utils');
```

Will require the entire library, including **all** available methods grouped under their respective method categories.

For example, the following are equivalent (but will result in varying sized [webpack](http://webpack.github.io/) builds)

### Full Library (results in **largest** build)
```js
var executeMultiple = require('fluxible-action-utils').async.executeMultiple;
```

### Category
```js
var executeMultiple = require('fluxible-action-utils/async').executeMultiple;
```

### Method (results in smallest build)
```js
var executeMultiple = require('fluxible-action-utils/async/executeMultiple');
```

:rotating_light: **WARNING** :rotating_light:

Methods inside the `internals` directory/category are not explicitely exported and are considered unstable.

require externally at your own risk as breaking changes inside `internals` will not be considered `breaking` for the library.

===

## API
* [`async`](#async)
- [`executeMultiple`](#executemultiple-context-actions-done)
- [`exectueCritical`](#executecritical-context-actions-done)

===

### async
*available as of v0.2.0*

```js
var asyncActionUtils = require('fluxible-action-utils/async');
```

Methods grouped under the `async` category are concerned with providing methods that aid in managing the asynchronous control flow of [`fluxible`](http://fluxible.io) actions.

[async.auto](https://github.com/caolan/async#autotasks-callback) is used under the hood to do the actual heavy lifting (thanks to [@caolan](https://github.com/caolan))

#### executeMultiple (context, actions, [done])
*available as of v0.2.0*

```js
var executeMultiple = require('fluxible-action-utils/async/executeMultiple');
```

Utility method used to execute multiple actions in parallel where possible. Each key in `actions` represents a `task` to be executed (and should be unique).

Expand All @@ -34,16 +85,16 @@ Signature `function (err, results)`

For each task that fails, the error returned will be aggregated under `err[task]`.

Example
**Example**

```js
// initHome.js

var actionUtils = require('fluxible-action-utils');
//
var executeMultiple = require('fluxible-action-utils/async/executeMultiple');
var UserStore = require('app/stores/UserStore');

module.exports = function initHome(context, params, done) {
actionUtils.executeMultiple(context, {
executeMultiple(context, {
loadUH: require('UH').actions.load,
loadUser: {
action: require('app/actions/loadUser'),
Expand Down Expand Up @@ -81,10 +132,15 @@ module.exports = function initHome(context, params, done) {
});
};
```
### executeCritical(context, actions, [done])
*available as of v0.1.0*

executeCritical allows you to execute a group of actions that are ALL deemed critical. This is a simple shorthand for executeMultiple when a group of actions are all critical.
#### executeCritical (context, actions, [done])
*available as of v0.2.0*

```js
var executeCritical = require('fluxible-action-utils/async/executeCritical');
```

`executeCritical` allows you to execute a group of actions that are **ALL** deemed critical. This is a simple shorthand for `executeMultiple` when a group of actions are all critical.

## Thanks
* [@mridgway](https://github.com/mridgway)
Expand Down
13 changes: 13 additions & 0 deletions async.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/*
* Copyright (c) 2015, Yahoo Inc. All rights reserved.
* Copyrights licensed under the New BSD License.
* See the accompanying LICENSE file for terms.
*/

'use strict';

module.exports = {
executeCritical: require('./async/executeCritical'),
executeMultiple: require('./async/executeMultiple'),
toAsyncTask: require('./async/toAsyncTask')
};
63 changes: 63 additions & 0 deletions async/executeCritical.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
* Copyright (c) 2015, Yahoo Inc. All rights reserved.
* Copyrights licensed under the New BSD License.
* See the accompanying LICENSE file for terms.
*/

'use strict';

var async = require('async');
var getTasksForAsync = require('./../internals/getTasksForAsync');

/**
* execute multiple actions that are deemed "critical" to the page.
* This method uses a Task interface similar
* to "async.auto" but expects actions (or action "objects") instead of
* async tasks. Once each action is complete(without error), the "FLUSH" event is dispatched.
*
* @see https://github.com/caolan/async#autotasks-callback
*
* @method executeCritical
*
* @param {FluxibleActionContext} context The fluxible action context
*
* @param {Object.<string, FluxAction>|Object.<string, Object>|Object.<string, Array>} actions The actions to
* execute
*
* @param {FluxibleAction} actions.$TASKNAME This is a non-critical action that doesn't take any params.
*
* @param {Object} actions.$TASKNAME This is an action Object
*
* @param {FluxibleAction} actions.$TASKNAME.action An action to execute
*
* @param {Boolean} [actions.$TASKNAME.isCritical=false] if the action is critical or not. If true, all other
* actions will not execute if this action fails.
*
* @param {*} [actions.$TASKNAME.params] Passed to the action as params
*
* @param {Array<String|FluxAction>} actions.$TASKNAME Contains the actions that need to run first before running
* the current task (defined as strings). The last entry in
* the array should either be a {FluxAction} or an action
* object as defined above.
*
* @param {Function} [done] done(err, results) An optional callback which is called
* when all the actions have been completed. It receives the
* err argument if any critical actions pass an error to their
* callback. Results are always returned; however, if an error
* occurs, no further actions will be performed, and the
* results object will only contain partial results. For each
* non-critical task that errors, the err object will contain
* the error under err.taskName
*
* @return {void}
*/
module.exports = function executeCritical (context, actions, done) {
var tasks = getTasksForAsync(context, actions, true);

if (!done) {
async.auto(tasks);
return;
}

async.auto(tasks, done);
};
83 changes: 83 additions & 0 deletions async/executeMultiple.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/*
* Copyright (c) 2015, Yahoo Inc. All rights reserved.
* Copyrights licensed under the New BSD License.
* See the accompanying LICENSE file for terms.
*/

'use strict';

var async = require('async');
var getTasksForAsync = require('./../internals/getTasksForAsync');

/**
* execute multiple actions.
* This method uses a Task interface similar
* to "async.auto" but expects actions (or action "objects") instead of
* async tasks.
*
* @see https://github.com/caolan/async#autotasks-callback
*
* @method executeMultiple
*
* @param {FluxibleActionContext} context The fluxible action context
*
* @param {Object.<string, FluxAction>|Object.<string, Object>|Object.<string, Array>} actions The actions to
* execute
*
* @param {FluxibleAction} actions.$TASKNAME This is a non-critical action that doesn't take any params.
*
* @param {Object} actions.$TASKNAME This is an action Object
*
* @param {FluxibleAction} actions.$TASKNAME.action An action to execute
*
* @param {Boolean} [actions.$TASKNAME.isCritical=false] if the action is critical or not. If true, all other
* actions will not execute if this action fails.
*
* @param {*} [actions.$TASKNAME.params] Passed to the action as params
*
* @param {Array<String|FluxAction>} actions.$TASKNAME Contains the actions that need to run first before running
* the current task (defined as strings). The last entry in
* the array should either be a {FluxAction} or an action
* object as defined above.
*
* @param {Function} [done] done(err, results) An optional callback which is called
* when all the actions have been completed. It receives the
* err argument if any critical actions pass an error to their
* callback. Results are always returned; however, if an error
* occurs, no further actions will be performed, and the
* results object will only contain partial results. For each
* non-critical task that errors, the err object will contain
* the error under err.taskName
*
* @return {void}
*/
module.exports = function executeMultiple (context, actions, done) {
var tasks = getTasksForAsync(context, actions, false);

if (!done) {
async.auto(tasks);
return;
}

async.auto(tasks, function (err, results) {
Object.keys(results).forEach(function (taskName) {
var taskErr = null;
if (results[taskName]) {
taskErr = results[taskName].err;
// result for task is actually an error
if (taskErr) {
// safety first
err = err || {};

// aggregate under err instead of results
err[taskName] = taskErr;

// clear results[taskName];
results[taskName] = null;
}
}
});

done(err, results);
});
};
43 changes: 43 additions & 0 deletions async/toAsyncTask.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* Copyright (c) 2015, Yahoo Inc. All rights reserved.
* Copyrights licensed under the New BSD License.
* See the accompanying LICENSE file for terms.
*/

'use strict';

var continueOnError = require('./../internals/continueOnError');
var createCriticalCB = require('./../internals/createCriticalCB');

/**
* converts a fluxible action into a task that can be passed to the "async" library.
*
* @method toAsyncTask
*
* @param {FluxibleAction} action The action to convert
*
* @param {FluxibleActionContext} context The fluxible action context
*
* @param {*} params Parameters passed to the fluxible action when calling it
*
* @param {Boolean} [isCritical=false] Whether the action is critical or not.
* critical actions will allow the action being executed to pass an error to the
* "done" callback passed into them. This will usually short-circuit all async
* tasks being executed in the same block. By default, errors passed by the
* action are passed as valid "data" under an "err" property.
*
* @param {String} taskName The name of the task
*
* @return {void}
*/
module.exports = function toAsyncTask (action, context, params, isCritical, taskName) {
return function (cb) {
if (!isCritical) {
cb = continueOnError(cb);
} else {
cb = createCriticalCB(cb, taskName);
}

context.executeAction(action, params, cb);
};
};
4 changes: 3 additions & 1 deletion index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,6 @@

'use strict';

module.exports = require('./libs/fluxible-action-utils');
module.exports = {
async: require('./async')
};
File renamed without changes.
File renamed without changes.
47 changes: 47 additions & 0 deletions internals/getTasksForAsync.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* Copyright (c) 2015, Yahoo Inc. All rights reserved.
* Copyrights licensed under the New BSD License.
* See the accompanying LICENSE file for terms.
*/

'use strict';

var toAsyncTask = require('./../async/toAsyncTask');

/**
* Converts a list of action objects to a format accepted by async.auto.
*
* @method getTasksForAsync
*
* @param {FluxibleActionContext} context The fluxible action context
*
* @param {Object} actions The actions to convert
*
* @param {Boolean} isAlwaysCritical True if every action is critical, else false
*
* @return {Object<String, tasks>} An object of valid async.auto tasks.
*/
module.exports = function getTasksForAsync (context, actions, isAlwaysCritical) {
var tasks = {};
// iterate over the actions
Object.keys(actions).forEach(function (taskName) {
var task = actions[taskName];

var actualTask = null;

tasks[taskName] = [].concat(actions[taskName]);
task = tasks[taskName];

// task value is an array, the last element will contain the actual action object
actualTask = task[task.length - 1];

// the last task was an action instead of an object
if (!actualTask.action) {
actualTask.action = actualTask;
}

tasks[taskName][task.length - 1] = toAsyncTask(actualTask.action,
context, actualTask.params, isAlwaysCritical || actualTask.isCritical || false, taskName);
});
return tasks;
};
Loading