简体   繁体   中英

Javascript: Throw in async chained methods

I've got a class that returns several methods to be chained like this:

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

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

Conditions are certain checks that may be true or false depending on the internal state of the class

The idea is that I can execute several methods like this:

myInstance.foo().bar();

This way, if any of the conditions in any method fails, this statement will throw an error.

However, now I'm trying to check conditions that require a promise to be resolved:

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

I want to maintain the same syntax as before (adding an await/then), I don't mind changing foo and bar to be async as well, as long as the following syntax is valid:

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

Is there a way of doing this? Note that there may be more than one asyncCheck and may be called in any order

You can maybe do it with some major hacks...

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

You could achieve this using an internal Promise field and reassigning it each time you call a method:

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

After a lot of testing, I found a possible solution extending the class with Promise, ending up with something like this:

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

The main tradeoff is that the constructor requires a dummy callback due to how extending promises work.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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