使用异步方法保持对象可链接

Zen*_*noo 5 javascript asynchronous method-chaining

假设我有一类Test包含大约10-20个方法的方法,所有方法都是可链接的。

在另一种方法中,我需要执行一些异步工作。

let test = new Test();
console.log(test.something()); // Test
console.log(test.asynch()); // undefined since the async code isn't done yet
console.log(test.asynch().something()); // ERROR > My goal is to make this 
Run Code Online (Sandbox Code Playgroud)

由于其他所有方法都是可链接的,因此,如果不是这种唯一的方法,我觉得对用户来说很奇怪。

我有办法保持班级的可链接主题吗?


我已经想通过下一个方法在这个方法的参数内的回调函数,但它不是真正的链接。

test.asynch(() => something())
Run Code Online (Sandbox Code Playgroud)

与相同Promises,它并不是真正的链接。

test.asynch().then(() => something())
Run Code Online (Sandbox Code Playgroud)

我想要的结果是

test.asynch().something()
Run Code Online (Sandbox Code Playgroud)

这是一个演示我的问题的代码段:

let test = new Test();
console.log(test.something()); // Test
console.log(test.asynch()); // undefined since the async code isn't done yet
console.log(test.asynch().something()); // ERROR > My goal is to make this 
Run Code Online (Sandbox Code Playgroud)

t.n*_*ese 2

我怀疑做这样的事情是否真的是个好主意。但是,如果原始对象满足某些条件,则使用代理将允许创建这样的行为。我强烈建议不要这样做。

请注意,此代码是一个概念证明,表明它在某种程度上是可能的,但不关心边缘情况,并且很可能会破坏某些功能。

一个代理用于包装原始类Test,以便可以修补每个实例以使它们可链接。

第二个将修补每个函数调用并为这些函数调用创建一个队列,以便按顺序调用它们。

    class Test {
      /**
       * Executes some async code
       * @returns {Test} The current {@link Test}
       */
      asynch() {
        console.log('asynch')
        return new Promise((resolve, reject) => setTimeout(resolve, 1000))
      }

      /**
       * Executes some code
       * @returns {Test} The current {@link Test}
       */
      something() {
        console.log('something')

        return this
      }
    }


    var TestChainable = new Proxy(Test, {
      construct(target, args) {
        return new Proxy(new target(...args), {

          // a promise used for chaining
          pendingPromise: Promise.resolve(),

          get(target, key, receiver) {
            //  intercept each get on the object
            if (key === 'then' || key === 'catch') {
              // if then/catch is requested, return the chaining promise
              return (...args2) => {
                return this.pendingPromise[key](...args2)
              }
            } else if (target[key] instanceof Function) {
              // otherwise chain with the "chainingPromise" 
              // and call the original function as soon
              // as the previous call finished 
              return (...args2) => {
                this.pendingPromise = this.pendingPromise.then(() => {
                  target[key](...args2)
                })

                console.log('calling ', key)

                // return the proxy so that chaining can continue
                return receiver
              }
            } else {
              // if it is not a function then just return it
              return target[key]
            }
          }
        })
      }
    });

    var t = new TestChainable
    t.asynch()
      .something()
      .asynch()
      .asynch()
      .then(() => {
        console.log('all calles are finished')
      })
Run Code Online (Sandbox Code Playgroud)