【问题标题】:Javascript: Throw in async chained methodsJavascript:抛出异步链式方法
【发布时间】:2018-06-19 16:47:16
【问题描述】:

我有一个类,它返回几个像这样链接的方法:

class MyClass{
  foo(){
    if(condition) return this;
    else throw;
  }

  bar(){
    if(condition) return this;
    else throw;
  }
}

条件是根据类的内部状态可能为真或假的某些检查

这个想法是我可以执行几个这样的方法:

myInstance.foo().bar();

这样,如果任何方法中的任何条件失败,该语句都会抛出错误。

但是,现在我正在尝试检查需要解决承诺的条件:

class MyClass2{
  asynCheck(){
     myPromise.then((condition)=>{
        ... // returning a promise won't work
     });

我想保持与以前相同的语法(添加等待/then),我不介意将 foo 和 bar 也更改为异步,只要以下语法有效:

await myInstance.foo().asyncCheck().bar();

有没有办法做到这一点?请注意,可能有多个 asyncCheck,并且可以按任意顺序调用

【问题讨论】:

  • 有什么问题?如果foobar 保持同步,并且链中只有最后一个方法是[可能] 异步的,它将正常工作。即使最后一个方法是同步的,await 也不会卡住它。
  • asyncCheck 可能在中间,async 方法不止一种。我将对其进行编辑以澄清
  • 它必须是具有承诺的功能......否则你必须在每次检查之前赶上
  • 或者你明小姐明白promise是如何工作的。尝试在 Promise 中返回 Promise,你会发现它会无缝链接
  • 它不会链接,@Akxe,在最后一个例子中,如果asyncCheck中返回了一个promise,它将尝试执行返回的promise的方法栏,它不会存在

标签: javascript promise async-await


【解决方案1】:

你也许可以通过一些主要的黑客来做到这一点......

class MyClass {
  foo() {
    const ret = new Promise((resolve, reject) => {
      resolve(1);
    })
    ret.bar = this.bar.bind(this);
    return ret;
  }

  bar() {
    const ret = new Promise((resolve, reject) => {
      resolve(2);
    })
    // hack `ret` to copy the methods over...
    return ret;
  }
}

(async() => {
  const c = new MyClass;
  console.log(await c.foo())
  console.log(await c.foo().bar())

})()

【讨论】:

  • 如果您这样做,那么c.foo().bar() 将调用bar c.foo() 返回的Promise 解决。我认为这不是 OP 想要的。您可以调整 hack 以使其正常工作。类似ret.bar = async (...args) => { await ret; return this.bar.apply( args ); }
  • @Paulpro 好点。我认为那是不可行的(没有像你建议的那样等待)
  • 我认为您可以调整答案中的技巧。我编辑了上面的评论,但还没有仔细考虑。
  • 我正在尝试一个 hack,它有点像你的,但反过来,试图用 Promise 扩展 MyClass
【解决方案2】:

您可以使用内部 Promise 字段并在每次调用方法时重新分配它:

class MyClass {
  constructor(promise) {
    if (!promise) this.promise = Promise.resolve();
    else this.promise = promise;
  }
  foo(condition) {
    this.promise = this.promise.then(() => {
        console.log("foo");
        if (!condition) throw 'foo error';
    });
    return this;
  }
  bar(condition) {
    this.promise = this.promise.then(() => {
        console.log("bar");
        if (!condition) throw 'bar error';
    });
    return this;
  }
  asynCheck(conditionPromise) {
    this.promise = this.promise.then(() => {
        return conditionPromise.then(condition => {
          console.log("asynCheck");
          if (!condition) throw 'asynCheck error';
        });
    });
    return this;
  }
  catch(callback) {
    this.promise.catch(callback);
  }
}

let instance = new MyClass();

instance.foo(true)
        .asynCheck(new Promise((res, rej) => setTimeout(() => res(true), 2000)))
        .bar(true)
        .asynCheck(new Promise((res, rej) => setTimeout(() => res(false), 3000)))
        .foo(false)
        .catch(e => console.log(e));

【讨论】:

  • 这能以某种方式使用 async/await 语句而不是最后的 catch 吗?
  • @angrykoala 你可以试试,比如await instance.foo(true).bar(false).promise
【解决方案3】:

经过大量测试,我找到了一个可能的解决方案,用 Promise 扩展类,结果如下:

class MyClass extends Promise {
  constructor(executor){
    super((resolve, reject)=>{return executor(resolve,reject)});
  }
  foo(){
    return new MyClass((resolve, reject)=>{
      this.then(()=>{
        if(condition) resolve();
        else reject();
      }).catch(()=> reject());
    })
  }

  asyncCheck(){
    return new MyClass((resolve, reject)=>{
      this.then(()=>{
         asyncCondition.then((condition)=>{
             if(condition) resolve();
             else reject();
         });
      }).catch(()=> reject());
    })
  }
}
const bar=new MyClass((r)=>{r()});
await bar.foo().asyncCheck();

主要的权衡是由于扩展承诺的工作方式,构造函数需要一个虚拟回调。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-03-28
    • 2016-12-26
    • 2014-05-08
    • 2014-05-15
    • 1970-01-01
    • 2016-07-10
    • 1970-01-01
    相关资源
    最近更新 更多