Chaining promises without using 'then'

前端 未结 4 2053
感动是毒
感动是毒 2020-12-19 21:12

I have an object (foo) that exposes several methods as promises (using jQuery deferred). The way I did it ended up with this kind of code:

var f         


        
相关标签:
4条回答
  • 2020-12-19 21:33

    Yes sure, you just need to extend your Deferreds to have these methods:

    function MyRpc { // if you can use ES2015 - this should be a `class`
      this._deferred = new $.Deferred();
    }
    // teach it to be a promise
    MyRpc.prototype.then = function(onFulfilled, onRejected) {
      return this._deferred.then(onFulfilled, onRejected);
    };
    
    // teach it to be a deferred
    MyRpc.protototype.resolve = function(arg) {
      this._deferred.resolve(arg);
    };
    
    MyRpc.prototype.reject = function(arg) {
      this._deferred.reject(arg);
    };
    
    // define your methods!
    
    MyRpc.prototype.method1 = function(arg) {
      var p = this._deferred.then(function(value) {
        // logic of `method1` from foo.method1 here
      });
      var rpc = new MyRpc(); // our continuation;
      p.then(function(v) { rpc.resolve(v) }, function(e) { rpc.reject(e); });
      return rpc;
    };
    

    Of course, with a real promise library all this is a lot easier than with jQuery's minimal promises.

    This would let you do:

    var rpc = new MyRpc();
    rpc.method1(1).method1(2).method1(3); // can also `.then` here
    

    I'm not sure it's worth it, but it works.

    0 讨论(0)
  • 2020-12-19 21:37

    I'm not really aware of Promises (so forgive my example if there is some implementation error) but this is possible using ES6 Proxy.

    At this time it's only available on the latest browsers so it might not meet your requirements.

    A Proxy allow to add a callback function on each object operations, it mean that you can retrieve the name of the called method to do what you want. In your case you want to call $.when() with the first promise and call .then() with others as parameter.

    "use strict";
    function createNewFoo(){
        let foo = new Foo();
    
        let proxy = new Proxy(foo, {
    
            // @param target : Object on which the operation will be made
            // @param name : property name
            get(target, name) {
    
                let promise = target[name];
    
                return function (...args) {
    
                    if (typeof promise === "function"){
                        promise = promise.apply(target, args);
                    }
                    else if (!(promise instanceof Promise)){
                        throw 'Can\'t handle "'+name+'"';
                    }
    
                    // Perform chaining
                    if (!target.promise){
                        target.promise = $.when(promise);
                    }
                    else{
                        target.promise.then(promise);
                    }
                    return proxy;
                };
            }
        });
        // Storing the Foo instance in the Proxy object, could be implemented in other way.
        proxy._target = foo;
        return proxy;
    }
    

    Assuming you're using a "class" defined as bellow

    function Foo(){
        // storing the promise result
        this.promise = null;
    
        this.method1 = function(arg){ 
            return new Promise(function(resolve, reject) {
                resolve("1");
            }); 
        }
    
        this.method2 = new Promise(function(resolve, reject) {
            resolve("2");
        });
        this.method3 = new Promise(function(resolve, reject) {
            resolve("3");
        });
    }
    

    You can now use this piece of code to chain your promises

    var fooProxy = createNewFoo()
        .method1(1)
        .method2()
        .method3();
    

    And retrieve the original Foo instance

    fooProxy._target 
    
    0 讨论(0)
  • 2020-12-19 21:39

    You will need to return a custom object with the methods you need, and let it have a promise for the state instead of the state as properties directly. In every of your methods, you'd need to call then on the wrapped promise, and return another instance that wraps a new promise for the new state (method result).

    function Foo(promise) {
        // make every instance a thenable:
        this.then = promise.then.bind(promise);
        // alternatively store the promise itself as a property and always call this.promise.then
    }
    function createNewFoo() {
        return new Foo($.when({/* initial state */}));
    }
    Foo.prototype.method1 = function method1(args) {
        return new Foo(this.then(function(curstate) {
            // method logic here
            // should `return` a new state - or a promise for it
        });
    };
    Foo.prototype.method2 = …;
    

    This is similar to the approaches outlined by Benjamin Gruenbaum and Louy, but with a much simpler implementation.

    0 讨论(0)
  • 2020-12-19 21:56

    Create your own promise.

    function MyPromise(resolver) {
      var _promise = new Promise(resolver);
      this.then = function(onFulfilled, onRejected) {
        var _p = _promise.then(onFulfilled, onRejected);
        return new MyPromise(_p.then.bind(_p));
      };
    }
    

    Add whatever methods you want...

    MyPromise.prototype.doSomething = function() {
      return this.then(/*...*/);
    };
    

    voila!

    new MyPromise()
      .then(/*...*/).doSomething()
      .then(/*...*/).doSomething(); // etc...
    

    Bonus:

    ['resolve', 'reject', 'all'].forEach(function(method) {
      MyPromise[method] = function(...args) {
        var promise = Promise[method](...args);
        return new MyPromise(promise.then.bind(promise));
      };
    });
    

    Also in ES6:

    class MyPromise {
      constructor(resolver) {
        var _promise = new Promise(resolver);
        this.then = function(onFulfilled, onRejected) {
          var _p = _promise.then(onFulfilled, onRejected);
          return new MyPromise(_p.then.bind(_p));
        };
      }
      doSomething() {
        return this.then(/*...*/);
      }
    }
    
    0 讨论(0)
提交回复
热议问题