Skip to content

Commit

Permalink
Merge pull request facebook#1556 from joshma/flux-dispatcher-docs
Browse files Browse the repository at this point in the history
Update Flux Dispatcher.dispatch and waitFor examples
  • Loading branch information
sophiebits committed May 24, 2014
2 parents 3e30940 + 7954a86 commit 1e268c3
Show file tree
Hide file tree
Showing 4 changed files with 127 additions and 45 deletions.
38 changes: 23 additions & 15 deletions docs/docs/flux-todo-list.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,13 +74,6 @@ var _addPromise = function(callback, payload) {
}));
};

/**
* Empty the queue of callback invocation promises.
*/
var _clearPromises = function() {
_promises = [];
};

var Dispatcher = function() {};
Dispatcher.prototype = merge(Dispatcher.prototype, {

Expand All @@ -99,12 +92,27 @@ Dispatcher.prototype = merge(Dispatcher.prototype, {
* @param {object} payload The data from the action.
*/
dispatch: function(payload) {
_callbacks.forEach(function(callback) {
_addPromise(callback, payload);
// First create array of promises for callbacks to reference.
var resolves = [];
var rejects = [];
_promises = _callbacks.map(function(_, i) {
return new Promise(function(resolve, reject) {
resolves[i] = resolve;
rejects[i] = reject;
});
});
// Dispatch to callbacks and resolve/reject promises.
_callbacks.forEach(function(callback, i) {
// Callback can return an obj, to resolve, or a promise, to chain.
// See waitFor() for why this might be useful.
Promise.resolve(callback(payload)).then(function() {
resolves[i](payload);
}, function() {
rejects[i](new Error('Dispatcher callback unsuccessful'));
});
});
Promise.all(_promises).then(_clearPromises);
_promises = [];
}

});

module.exports = Dispatcher;
Expand Down Expand Up @@ -583,18 +591,18 @@ Adding Dependency Management to the Dispatcher

As I said previously, our Dispatcher implementation is a bit naive. It's pretty good, but it will not suffice for most applications. We need a way to be able to manage dependencies between Stores. Let's add that functionality with a waitFor() method within the main body of the Dispatcher class.

We'll need another public method, waitFor().
We'll need another public method, waitFor(). Note that it returns a Promise that can in turn be returned from the Store callback.

```javascript
/**
* @param {array} promisesIndexes
* @param {function} callback
*/
waitFor: function(promiseIndexes, callback) {
var selectedPromises = _promises.filter(function(/*object*/ _, /*number*/ j) {
return promiseIndexes.indexOf(j) !== -1;
var selectedPromises = promiseIndexes.map(function(index) {
return _promises[index];
});
Promise.all(selectedPromises).then(callback);
return Promise.all(selectedPromises).then(callback);
}
```

Expand Down
50 changes: 22 additions & 28 deletions examples/todomvc-flux/js/dispatcher/Dispatcher.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,28 +27,6 @@ var merge = require('react/lib/merge');
var _callbacks = [];
var _promises = [];

/**
* Add a promise to the queue of callback invocation promises.
* @param {function} callback The Store's registered callback.
* @param {object} payload The data from the Action.
*/
var _addPromise = function(callback, payload) {
_promises.push(new Promise(function(resolve, reject) {
if (callback(payload)) {
resolve(payload);
} else {
reject(new Error('Dispatcher callback unsuccessful'));
}
}));
};

/**
* Empty the queue of callback invocation promises.
*/
var _clearPromises = function() {
_promises = [];
};

var Dispatcher = function() {};
Dispatcher.prototype = merge(Dispatcher.prototype, {

Expand All @@ -67,10 +45,26 @@ Dispatcher.prototype = merge(Dispatcher.prototype, {
* @param {object} payload The data from the action.
*/
dispatch: function(payload) {
_callbacks.forEach(function(callback) {
_addPromise(callback, payload);
// First create array of promises for callbacks to reference.
var resolves = [];
var rejects = [];
_promises = _callbacks.map(function(_, i) {
return new Promise(function(resolve, reject) {
resolves[i] = resolve;
rejects[i] = reject;
});
});
// Dispatch to callbacks and resolve/reject promises.
_callbacks.forEach(function(callback, i) {
// Callback can return an obj, to resolve, or a promise, to chain.
// See waitFor() for why this might be useful.
Promise.resolve(callback(payload)).then(function() {
resolves[i](payload);
}, function() {
rejects[i](new Error('Dispatcher callback unsuccessful'));
});
});
Promise.all(_promises).then(_clearPromises);
_promises = [];
},

/**
Expand Down Expand Up @@ -108,10 +102,10 @@ Dispatcher.prototype = merge(Dispatcher.prototype, {
* A more robust Dispatcher would issue a warning in this scenario.
*/
waitFor: function(/*array*/ promiseIndexes, /*function*/ callback) {
var selectedPromises = promiseIndexes.map(function(idx) {
return _promises[idx];
var selectedPromises = promiseIndexes.map(function(index) {
return _promises[index];
});
Promise.all(selectedPromises).then(callback);
return Promise.all(selectedPromises).then(callback);
}

});
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
"use strict";

jest.autoMockOff();

describe('AppDispatcher', function() {
var AppDispatcher;

beforeEach(function() {
AppDispatcher = require('../AppDispatcher.js');
});

it('sends actions to subscribers', function() {
var listener = jest.genMockFunction();
AppDispatcher.register(listener);

var payload = {};
AppDispatcher.dispatch(payload);
expect(listener.mock.calls.length).toBe(1);
expect(listener.mock.calls[0][0]).toBe(payload);
});

it.only('waits with chained dependencies properly', function() {
var payload = {};

var listener1Done = false;
var listener1 = function(pl) {
return AppDispatcher.waitFor([index2, index4], function() {
// Second, third, and fourth listeners should have now been called
expect(listener2Done).toBe(true);
expect(listener3Done).toBe(true);
expect(listener4Done).toBe(true);
listener1Done = true;
});
};
var index1 = AppDispatcher.register(listener1);

var listener2Done = false;
var listener2 = function(pl) {
return AppDispatcher.waitFor([index3], function() {
expect(listener3Done).toBe(true);
listener2Done = true;
});
};
var index2 = AppDispatcher.register(listener2);

var listener3Done = false;
var listener3 = function(pl) {
listener3Done = true;
return true;
};
var index3 = AppDispatcher.register(listener3);

var listener4Done = false;
var listener4 = function(pl) {
return AppDispatcher.waitFor([index3], function() {
expect(listener3Done).toBe(true);
listener4Done = true;
});
};
var index4 = AppDispatcher.register(listener4);

runs(function() {
AppDispatcher.dispatch(payload);
});

waitsFor(function() {
return listener1Done;
}, "Not all subscribers were properly called", 500);

runs(function() {
expect(listener1Done).toBe(true);
expect(listener2Done).toBe(true);
expect(listener3Done).toBe(true);
});
});
});
8 changes: 6 additions & 2 deletions examples/todomvc-flux/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,14 @@
"reactify": "~0.4.0",
"statics": "~0.1.0",
"uglify-js": "~2.4.13",
"watchify": "~0.4.1"
"watchify": "~0.4.1",
"jest-cli": "~0.1.5"
},
"scripts": {
"start": "STATIC_ROOT=./static watchify -o js/bundle.js -v -d .",
"build": "STATIC_ROOT=./static NODE_ENV=production browserify . | uglifyjs -cm > js/bundle.min.js",
"collect-static": "collect-static . ./static",
"test": "echo \"Error: no test specified\" && exit 1"
"test": "jest"
},
"author": "Bill Fisher",
"license": "Apache 2",
Expand All @@ -28,5 +29,8 @@
"reactify",
"envify"
]
},
"jest": {
"rootDir": "./js"
}
}

0 comments on commit 1e268c3

Please sign in to comment.