链接承诺而不使用'然后'

时间:2016-03-03 14:22:35

标签: javascript jquery promise chaining

我有一个对象(foo),它将几个方法公开为promises(使用JQuery延迟)。我这样做的方式最终得到了这种代码:

var foo = createNewFoo();
$.when(foo.method1(arg))
    .then(foo.method2)
    .then(foo.method3);

我希望将代码重构为更好的代码,例如:

var foo = createNewFoo()
    .method1(arg)
    .method2()
    .method3();

但我不确定如何实施foo以便这样做。

4 个答案:

答案 0 :(得分:2)

是的,您只需要扩展Deferred以获得这些方法:

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;
};

当然,使用真正的promise库,所有这些很多比使用jQuery的最小承诺更容易。

这可以让你这样做:

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

我不确定它是否值得,但它确实有效。

答案 1 :(得分:1)

您需要使用所需的方法返回自定义对象,并让它直接承诺状态而不是状态作为属性。在每个方法中,您需要在包装的promise上调用then,并返回包含新状态的新承诺的另一个实例(方法结果)。

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 = …;

这类似于Benjamin Gruenbaum和Louy概述的方法,但实施起来要简单得多。

答案 2 :(得分:0)

我并没有真正意识到Promises(如果有一些实现错误,请原谅我的例子)但是这可以使用ES6 Proxy

此时它仅适用于最新的浏览器,因此可能无法满足您的要求。

代理允许在每个对象操作上添加回调函数,这意味着您可以检索被调用方法的名称以执行您想要的操作。 在您的情况下,您希望使用第一个承诺拨打$.when(),并以其他人作为参数调用.then()

"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;
}

假设您正在使用"类"定义为吼叫

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");
    });
}

您现在可以使用这段代码链接您的承诺

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

并检索原始的Foo实例

fooProxy._target 

答案 3 :(得分:0)

创建自己的承诺。

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));
  };
}

添加您想要的任何方法......

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

瞧!

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

加成:

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

同样在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(/*...*/);
  }
}