简体   繁体   English

不使用“then”链接承诺

[英]Chaining promises without using 'then'

I have an object ( foo ) that exposes several methods as promises (using jQuery deferred).我有一个对象( foo ),它公开了几个方法作为承诺(使用 jQuery deferred)。 The way I did it ended up with this kind of code:我这样做的方式最终得到了这种代码:

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

I wish to refactor the below code to be nicer and look like this:我希望将下面的代码重构得更好,看起来像这样:

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

But I'm not sure how to implement foo that it would be possible.但我不确定如何实现foo是可能的。

Yes sure, you just need to extend your Deferred s to have these methods:是的,你只需要扩展你的Deferred s 来拥有这些方法:

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.当然,有了真正的 promise 库,这比使用 jQuery 的最小 promise容易得多。

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.我不确定这是否值得,但它确实有效。

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).在您的每个方法中,您都需要在包装好的承诺上调用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 = …;

This is similar to the approaches outlined by Benjamin Gruenbaum and Louy, but with a much simpler implementation.这类似于 Benjamin Gruenbaum 和 Louy 概述的方法,但实现要简单得多。

I'm not really aware of Promises (so forgive my example if there is some implementation error) but this is possible using ES6 Proxy .我不太了解 Promises(所以如果有一些实现错误,请原谅我的例子)但是使用 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.在你的情况下,你想用第一个承诺调用$.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;
}

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并检索原始 Foo 实例

fooProxy._target 

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM