1
\$\begingroup\$

implemented a basic asynchronous stack so, that I can do.

await stack.push(item);
const item = await stack.pop();

basically I'm registering listeners when ever i'm trying to pop an item over an empty stack and when ever i'm pushing an item i'm checking if there are any one waiting over for an item i'm sending it through the listener.

class AsyncStack {
  constructor() {
    this.stack = [];
    this.waiting_minions = [];
  }

  async push(item) {
    if (this.waiting_minions.length > 0) {
      const signal = this.waiting_minions.pop();
      await signal(item);
    } else {
      this.stack.push(item);
    }
  }

  length() {
    return this.stack.length;
  }

  async pop() {
    return new Promise((resolve) => {
      if (this.stack.length > 0) {
        return resolve(this.stack.pop());
      }
      this.waiting_minions.push(resolve);
    });
  }

}
\$\endgroup\$
1
\$\begingroup\$

You can use a getter to return the length.

get length() {
  return this.stack.length;
}

Then you can then get it like this stack.length instead of this stack.length().


To me it would make more sense to use waiting_minions like a queue, so the one that has been waiting the longest gets a value first.

const signal = this.waiting_minions.shift();

The return value of the executor (the function passed to a promise) is ignored. You only need to resolve (or reject) it.

resolve(this.stack.pop());

I haven't used async/await before, so I might be wrong about some of this, but I've looked at the docs and been playing around with your code a bit.

There is no need for push() to be async. You will immediately put the value on the stack or pass it to a waiting promise, so you never wait for anything. You don't need the await, since you resolve the promise, which you are already waiting for when you called await stack.pop()

pop() doesn't need to be async either. It needs to be async if you use await inside it, but not if you call it with await.

When using await, if the value is not a promise, it converts the value to a resolved promise. So pop() doesn't need to make a promise when it can take a value from the stack.

pop() {
  if (this.stack.length > 0) {
    return this.stack.pop();
  }
  return new Promise((resolve) => {
    this.waiting_minions.push(resolve);
  });
}

There are probably other ways to do this, but with the changes I made, it still worked as expected.

\$\endgroup\$
  • \$\begingroup\$ thanks for the review, nice idea on the getter, regarding the queue, I didn't want to complicate the implementation too much(implementing a efficient queue) for my use case is there any reason to not prefer async-await? I like to start with async await whenever possible, so that I don't have to rewrite the functions when I try to add more features which require async-await. \$\endgroup\$ – eightnoteight Mar 7 '18 at 16:55
  • \$\begingroup\$ If pop() is async you can do a one-liner return this.stack.length > 0 ? this.stack.pop() : await this.nextValue(); but that's just putting the promise part into a different function. I've tried coming up with a good way of using more async/await, but I don't have any ideas. \$\endgroup\$ – Kruga Mar 8 '18 at 9:42

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.