mobx-utils
Version:
Utility functions and common patterns for MobX
108 lines (107 loc) • 4.78 kB
JavaScript
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
import { flow } from "mobx";
import { deprecated } from "./utils";
/**
* _deprecated_ this functionality can now be found as `flow` in the mobx package. However, `flow` is not applicable as decorator, where `asyncAction` still is.
*
*
*
* `asyncAction` takes a generator function and automatically wraps all parts of the process in actions. See the examples below.
* `asyncAction` can be used both as decorator or to wrap functions.
*
* - It is important that `asyncAction should always be used with a generator function (recognizable as `function*` or `*name` syntax)
* - Each yield statement should return a Promise. The generator function will continue as soon as the promise settles, with the settled value
* - When the generator function finishes, you can return a normal value. The `asyncAction` wrapped function will always produce a promise delivering that value.
*
* When using the mobx devTools, an asyncAction will emit `action` events with names like:
* * `"fetchUsers - runid: 6 - init"`
* * `"fetchUsers - runid: 6 - yield 0"`
* * `"fetchUsers - runid: 6 - yield 1"`
*
* The `runId` represents the generator instance. In other words, if `fetchUsers` is invoked multiple times concurrently, the events with the same `runid` belong together.
* The `yield` number indicates the progress of the generator. `init` indicates spawning (it won't do anything, but you can find the original arguments of the `asyncAction` here).
* `yield 0` ... `yield n` indicates the code block that is now being executed. `yield 0` is before the first `yield`, `yield 1` after the first one etc. Note that yield numbers are not determined lexically but by the runtime flow.
*
* `asyncActions` requires `Promise` and `generators` to be available on the target environment. Polyfill `Promise` if needed. Both TypeScript and Babel can compile generator functions down to ES5.
*
* N.B. due to a [babel limitation](https://github.com/loganfsmyth/babel-plugin-transform-decorators-legacy/issues/26), in Babel generatos cannot be combined with decorators. See also [#70](https://github.com/mobxjs/mobx-utils/issues/70)
*
*
* @example
* import {asyncAction} from "mobx-utils"
*
* let users = []
*
* const fetchUsers = asyncAction("fetchUsers", function* (url) {
* const start = Date.now()
* const data = yield window.fetch(url)
* users = yield data.json()
* return start - Date.now()
* })
*
* fetchUsers("http://users.com").then(time => {
* console.dir("Got users", users, "in ", time, "ms")
* })
*
* @example
* import {asyncAction} from "mobx-utils"
*
* mobx.configure({ enforceActions: "observed" }) // don't allow state modifications outside actions
*
* class Store {
* \@observable githubProjects = []
* \@observable = "pending" // "pending" / "done" / "error"
*
* \@asyncAction
* *fetchProjects() { // <- note the star, this a generator function!
* this.githubProjects = []
* this.state = "pending"
* try {
* const projects = yield fetchGithubProjectsSomehow() // yield instead of await
* const filteredProjects = somePreprocessing(projects)
* // the asynchronous blocks will automatically be wrapped actions
* this.state = "done"
* this.githubProjects = filteredProjects
* } catch (error) {
* this.state = "error"
* }
* }
* }
*
* @export
* @returns {Promise}
*/
export function asyncAction(arg1, arg2) {
// decorator
if (typeof arguments[1] === "string") {
var name_1 = arguments[1];
var descriptor_1 = arguments[2];
if (descriptor_1 && descriptor_1.value) {
return Object.assign({}, descriptor_1, {
value: flow(descriptor_1.value),
});
}
else {
return Object.assign({}, descriptor_1, {
set: function (v) {
Object.defineProperty(this, name_1, __assign(__assign({}, descriptor_1), { value: flow(v) }));
},
});
}
}
// direct invocation
var generator = typeof arg1 === "string" ? arg2 : arg1;
var name = typeof arg1 === "string" ? arg1 : generator.name || "<unnamed async action>";
deprecated("asyncAction is deprecated. use mobx.flow instead");
return flow(generator); // name get's dropped..
}