简体   繁体   English

JavaScript中的可链接,基于Promise的类接口

[英]Chainable, Promise-based Class Interfaces in JavaScript

I am writing a constructor in JavaScript that has the following properties: 我在JavaScript中编写一个具有以下属性的构造函数:

function WhizBang() {

   var promise;

   this.publicMethod_One = function publicMethod_One() { ... };
   this.publicMethod_Two = function publicMethod_Two() { ... };

   promise = asyncInit();
}

So, calling new WhizBang() will start the asyncInit() process. 因此,调用new WhizBang()asyncInit()将启动asyncInit()过程。 What isn't obvious from the code above is that none of the public methods in the interface should run until this call to asyncInit() has closed. 从上面的代码中可以明显看出,在对asyncInit()调用关闭之前,接口中的所有公共方法都不应该运行。

So, the definition of publicMethod_One() might look something like this: 因此, publicMethod_One()的定义可能如下所示:

function publicMethod_One() {

  promise
    .then( doStuff )
    .catch( reportInitFailure )
  ;

  function doStuff() { ... }
  function reportInitFailure() { ... }
}

Some of the things that happen in doStuff() are asynchronous; doStuff()中发生的一些事情是异步的; some of them aren't. 其中一些不是。

So, if an end user of my class did something like this: 所以,如果我班级的最终用户做了这样的事情:

function main() {

  var whizBang = new WhizBang();

  whizBang
    .publicMethod_One()
    .publicMethod_Two()
  ;
}

The call to publicMethod_One() musn't be made until asyncInit() has closed. asyncInit()关闭之前, asyncInit()调用publicMethod_One() And the call to publicMethod_Two() musn't be made until both asyncInit() and publicMethod_One() have closed. 并且在asyncInit()publicMethod_One()都关闭之前, asyncInit()调用publicMethod_Two()

How can I define my class methods so that they are chainable? 如何定义我的类方法以使它们可链接?

What I think I need to do is define a class whose public methods are all equivalent to calling then() on a promise, followed by class specific, implementation stuff. 我认为我需要做的是定义一个类,其公共方法都等同于在promise上调用then() ,然后是类特定的实现内容。

Internets, halp! 互联网,停了!

( Bonus points for using the Bluebird Promise Library in your answer. ) (在你的答案中使用Bluebird Promise Library的奖励积分。)

What you have now 你现在有什么

What you have now is actually pretty nice. 你现在拥有的实际上非常好。 Since you're caching the result of asyncInit in a promise and everyone waits for the same promise - it's impossible for any of the code in those functions to run before the promise has finished. 因为你在一个promise中缓存了asyncInit的结果,并且每个人都在等待相同的承诺 - 这些函数中的任何代码都不可能在promise完成之前运行。

function publicMethod_One() {    
  promise // the fact you're starting with `promise.` means it'll wait
    .then( doStuff )
    .catch( reportInitFailure );
}

So rather than forcing people to wait in order to use publicMethod_One they already can call it right away and the methods like doStuff will only execute the promise has resolved. 因此,不是强迫人们等待使用publicMethod_One他们已经可以立即调用它,而像doStuff这样的方法只会执行已经解决的promise。

A fluid interface 流体界面

Well, like you noticed your code has a major issue, there is no way for a given method to know when to run or a way to sequence methods - you can solve this in two ways: 好吧,就像你注意到你的代码有一个主要问题一样,给定的方法无法知道何时运行或方法对方法进行排序 - 你可以通过两种方式解决这个问题:

  • Create a fluid interface that returns this on every action and queues the promise. 创建一个流体接口,在每个操作上返回this接口并对promise进行排队。
  • Return the promise from each async call. 从每个异步调用返回promise。

Let's look at the two approaches: 让我们看看这两种方法:

Fluid interface 流体界面

This means that all your methods return the instance, they must also queue so things don't happen 'at once'. 这意味着您的所有方法都返回实例,它们也必须排队,因此事情不会“立刻”发生。 We can accomplish this by modifying promise on each call: 我们可以通过修改每次调用的promise来实现这一点:

function publicMethod_One() {    
  promise = promise // note we're changing it
    .then( doStuff )
    .catch( reportInitFailure );
  return this; // and returning `this`
}

You might also want to expose a .ready method that returns the current promise so the sequence of operations can be waited for from the outside: 您可能还希望公开一个返回当前promise的.ready方法,以便可以从外部等待操作序列:

function ready(){
    return this.promise;
}

This can enable things like: 这可以实现以下目标:

var ready = new WhizBang().publicMethod_One().publicMethod_One().ready();
ready.then(function(){
     // whizbang finished all operations
}); // can also add error handler

Returning thenables 返回

In my opinion this is the simpler approach, all your methods return the promise they create so they can individually be waited for: 在我看来,这是更简单的方法,所有方法都返回它们创建的承诺,以便它们可以单独等待:

function publicMethod_One() {    
  return promise // note we're returning and not changing it
    .then( doStuff )
    .catch( reportInitFailure );
}

This is nice because the async operation is exposed outside. 这很好,因为异步操作暴露在外面。

Chaining is possible since you're using bluebird with .bind as such: 链接是可能的,因为你使用.bind bluebird:

var whiz = new WhizBang();
var res = Promise.bind(whiz).then(whiz.publicMethod_one)
                            .then(whiz.publicMethod_one);
res.then(function(){
     // all actions completed, can also use return values easier.
});

But the advantage is it easier to reason about from the outside - for this reason I prefer this approach personally. 但优点是从外部更容易推理 - 因此我更喜欢这种方法。 Personally I always prefer to return meaningful data than to change internal state. 就个人而言,我总是喜欢return有意义的数据,而不是改变内部状态。

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

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