最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

node.js - How to chain async methods in javascript ES6 classes - Stack Overflow

programmeradmin1浏览0评论

I want to chain methods from a class. I have o problems with synchronous methods, but I don't know how to do it with asynchronous methods.

For example, this class:

class Example {

  constructor() {
    this.val = 0
  }

  async () {
    setTimeout(() => {
      this.val += 1
      return this
    }, 5000)
  }

  sync () {
    this.val += 1
    return this
  }

  check () {
    console.log('checker', this.val)
    return this
  }

}

This works:

new Example().sync().check()
> 1

But this doesn't work:

new Example().async().check()
> TypeError: Cannot read property 'check' of undefined

P.S. I want chaining, not Hell Callbacks.

I want to chain methods from a class. I have o problems with synchronous methods, but I don't know how to do it with asynchronous methods.

For example, this class:

class Example {

  constructor() {
    this.val = 0
  }

  async () {
    setTimeout(() => {
      this.val += 1
      return this
    }, 5000)
  }

  sync () {
    this.val += 1
    return this
  }

  check () {
    console.log('checker', this.val)
    return this
  }

}

This works:

new Example().sync().check()
> 1

But this doesn't work:

new Example().async().check()
> TypeError: Cannot read property 'check' of undefined

P.S. I want chaining, not Hell Callbacks.

Share Improve this question edited Sep 18, 2015 at 15:25 ASGM 11.4k1 gold badge36 silver badges54 bronze badges asked Sep 18, 2015 at 15:23 rdbmaxrdbmax 411 gold badge1 silver badge3 bronze badges 5
  • 3 First of all, async is not ES6 but a proposal for ES7. – Bergi Commented Sep 18, 2015 at 15:30
  • 1 You should be using Promises, not attempting to shift this references around. You'll not have that syntax still (until async/await arrive in ES7) but at least you can avoid callback hell – CodingIntrigue Commented Sep 18, 2015 at 15:32
  • As an aside, read this for a little info about the execution context of a function given to setTimeout and why doing return this doesn't work like you want it to. – sdgluck Commented Sep 18, 2015 at 15:34
  • You'd need to keep track internally whether the async method is done or not and have check monitor that. Btw, this has nothing to do with ES6 or classes (except promises are ES6, but they existed before that as well). – Felix Kling Commented Sep 18, 2015 at 15:35
  • 1 If you want to chain async operations (like jQuery does with animations), then you can build a queue (or use promises) where each method can be stored until the previous async operation completes and can trigger the next operation and each async method and sync method that you want to work with this will have to notify the queue when they are done. – jfriend00 Commented Sep 18, 2015 at 15:35
Add a comment  | 

4 Answers 4

Reset to default 7

I expect that you want to call check() after the timeout has expired. The problem is that forks off, and you can't immediately have something available to return.

You could pass in check() as a callback:

class Example {

  constructor() {
    this.val = 0
  }

  async (callback) {
    setTimeout(() => {
      this.val += 1
      callback()
    }, 5000)
  }

  sync () {
    this.val += 1
    return this
  }

  check () {
    console.log('checker', this.val)
    return this
  }

}

// execution
var ex = new Example();
ex.async(ex.check)

... or a promise

class Example {

  constructor() {
    this.val = 0
  }

  async (callback) {
    var deferred = Q.defer()
    setTimeout(() => {
      this.val += 1
      deferred.resolve();
    }, 5000)
    return deferred.promise;
  }

  sync () {
    this.val += 1
    return this
  }

  check () {
    console.log('checker', this.val)
    return this
  }

}

// execution
var ex = new Example()
ex.async().then(() => ex.check())

... Or you could use ES6 generators

If all you want is new Example().async().check() to work, all you need to do is return this after you call setTimeout. Ex:

async () {
  setTimeout(() => {
    this.val += 1
  }, 5000)
  return this
}

The return this inside the timeout isn't necessary, since it'll be executing on its own. It's basically running stand alone at that point.

Really, if you want this whole thing to be running async completely, and you're able to control flow of when certain things occur, you need to be using promises to make that happen.

If you are using async functions and methods, you are using promises (I suppose you know them, if not please learn about them before reading on).

You should not use setTimeout inside of an asynchronous function if you consider waiting for it. Instead, create a promise for the timeout, preferably using a helper function like this:

function timeout(ms) {
    return new Promise(resolve => {
        setTimeout(resolve, ms)
    });
}

Now you can write your method like you'd really want to:

class Example {
  …
  async async () {
    await timeout(5000);
    this.val += 1;
    return this;
  }
  …
}

Of course, as an async function it does not return the instance itself, but a promise for it. If you are going to chain, you will have to call it inside of another asynchronous function where you can await that promise:

(async function() {
    (await (new Example().async())).check();
}());

What you are looking for will be solved most elegantly by Promises. You'll need to likely install a polyfill, like Bluebird or q.

I would change your async method to:

async() {
    return new Promise((resolve, reject)=>{
        setTimeout(()=>{
            this.val += 1;
            resolve(this);
        }, 5000);
    });
}

And your calling code to:

new Example().async().then((instance)=>instance.check());

Unfortunately, until ES7 async functions are nailed down, there won't be an elegant way to do this without some form of callback.

发布评论

评论列表(0)

  1. 暂无评论