1

I'm attempting to introduce a queue-type system to a JS class to allow for Async method chaining, ideally, I'd like to perform operations on the class instance using these async methods and return "this" instance.

export class Queue {
    constructor() {
        this.queue = Promise.resolve()
        this.firstRequestStarted = false
        this.firstRequestStatusCode  = 0
        this.secondRequestStarted = false
        this.secondRequestStatusCode = 0
    }
    then(callback) {
        callback(this.queue)
    }
    chain(callback) {
        return this.queue = this.queue.then(callback)
    }
    first() {
        this.chain(async () => {
            try {
                this.firstRequestStarted = true
                const response = await axios.get("https://stackoverflow.com/questions")
                this.firstRequestStatusCode = response.status
                return this
            }
            catch (e) {
                const { message = "" } = e || {}
                return Promise.reject({ message })
            }
        })
        return this
    }
    second() {
        this.chain(async () => {
            try {
                this.secondRequestStarted = true
                const response = await axios.get("https://stackoverflow.com/")
                this.secondRequestStatusCode = response.status
                return this
            }
            catch (e) {
                const { message = "" } = e || {}
                return Promise.reject({ message })
            }
        })
        return this
    }
}

Functions are added to the queue, and as we await them, the "then" method will handle their execution.

const x = await new Queue()
            .first()
            .second()
        console.log(x)

The challenge I'm facing is that I can never actually get "this" (instance of Queue) back to x.

1) x === undefined
2) "Chaining cycle detected for promise #<Promise>"
or ( I haven't been able to track down where this one is coming from, node error)
3) finished with exit code 130 (interrupted by signal 2: SIGINT)

I have tried adding a "consume" method, which simply returns "this", this leads to error #2 above

me() {
        this.chain( () => {
            try {
                return this
            }
            catch (e) {
                const { message = "" } = e || {}
                return Promise.reject({ message })
            }
        })
        return this
    }

The confusion on my part, is that if I use any value other than "this", it works as expected

me() {
        this.chain( () => {
            try {
                return "test"
            }
            catch (e) {
                const { message = "" } = e || {}
                return Promise.reject({ message })
            }
        })
        return this
    }

x === "test" I'm also able to return the values associated to this with something like the following return {...this}

Ideally, I'd like to return the instance of Queue to X, as I plan on modifying the properties of the Queue instance through my async methods, await them, and be returned with an "initialized" instance of Queue.

Any input would be greatly appreciated - thank you!

nvancouver
  • 137
  • 11
  • Does this answer your question? [Async/Await Class Constructor](https://stackoverflow.com/questions/43431550/async-await-class-constructor) – Christopher Oct 08 '22 at 19:40
  • What is your actual use case? It seems like one would always call `.first()`, then `.second()`? Is the order really arbitrary, are repetitions allowed? Why do you need to use chaining? – Bergi Oct 08 '22 at 20:22

1 Answers1

1

The problem is that your Queue instances are thenable (have a .then() method), and the promise is tried to be resolved with itself (this.queue). See also here or there.

You have two options:

  • Do not resolve your promise with the instance, but write

    const x = new Queue().first().second();
    await x;
    console.log(x);
    
  • remove the then method from your class, then call

    const x = new Queue().first().second().queue;
    console.log(x);
    

    (or possibly introduce a getter method - .get(), .toPromise() - instead of directly accessing .queue)

Bergi
  • 630,263
  • 148
  • 957
  • 1,375