angular-redux / store

Angular 2+ bindings for Redux
MIT License
1.34k stars 205 forks source link

Async @dispatch methods #534

Open Buslowicz opened 6 years ago

Buslowicz commented 6 years ago

This is a...

It would be really handy if methods decorated with @dispatch() decorator could be async methods (return a Promise). It would allow to use RxJS pipelines (with .toPromise()), or use various dialogs or after effects and whatever one would imagine. The implementation would be pretty trivial and wouldn't touch the actual dispatcher, just the decorator. The result would be checked for being a promise (simple instanceof could be used, however to make it compatible with bluebird and similar, a more complex check should be used). If it's not a promise, continue with what is done at the moment, otherwise execute current behavior inside the .then callback. If you guys have not much time to take care of that task, I could do it myself and make a PR, just want to know what you think of that and eventually agree on the specs. My proposal would only modify the wrapped function and is as follows (untested, if you spot an error do let me know, I will test it before PR though):

    function isPromise(object: any): object is Promise<any> {
      return typeof object === 'object' && typeof object.then === 'function' && typeof object.catch === 'function' && typeof object.finally === 'function';
    }

    const wrapped = function(this: any, ...args: any[]) {
      const result = originalMethod.apply(this, args);

      const dispatchAction = (finalResult: any) => {
        if (result === false) {
          return;
        }
        const store = getBaseStore(this) || NgRedux.instance;
        if (store) {
          store.dispatch(finalResult);
        }
      };

      if (isPromise(result)) {
        result.then(dispatchAction);
      } else {
        dispatchAction(result);
      }
      return result;
    };

And the usage:

  @dispatch() async methodA() {
    const someData = await this.getAsyncData();
    return { type: "SOME ACTION", payload: someData };
  }
  @dispatch() methodB() {
    return new Promise((resolve) => setTimeout(resolve, 1000, { type: "PING" }));
  }
  // etc...

So, any thoughts on that?

Buslowicz commented 5 years ago

I'd appreciate any feedback on this, should I PR it? Or would it not even be merged? Is this concept good enough, or should I make it different? Seriously the 3 weeks of total silence on this issue makes me believe this project is more dead than alive and pushes me towards competitors like ngrx. I am sure I am not the only one feeling so...