0
\$\begingroup\$

While studying Deno which is a kind of Node with everything normalized to modern JS and TS, I quickly met the;

import { serve } from "https://deno.land/std@0.87.0/http/server.ts";

const server = serve({ hostname: "0.0.0.0", port: 8080 });

for await (const request of server) {
  request.respond({ status: 200, body: "Whatever" });
}

Here the server object is an asynchronous iterator. Whenever a request is received it turns the for await of loop once. I believe server must be instantiated from an Asynchronously Iterable Queue type.

So i tried to implement a similar asynchronous Queue over the best synchronous Queue type in JS that i am aware of.

Obviously, in an AsyncQueue we can not have .dequeue() or .peek() functions since in this case dequeueing happens automatically by the resolution of the first promise and there is no point to peek at the first promise in a Queue of promises. Instead here we have a .next() function for dequeueing alongside with an .enqueue() function to insert new promises.

An important aspect of this AsyncQueue is that it must be able to accept new promises both sychronously and asynchronously which means it will run forever.

So the code is below and i would like to know if it can be simplified any further or perhaps there are things to enhance the performance or whatnot. Please bear with my indentation style since i will not change it. Also i like to use the Private Class Fields very much.

class AsyncQueue {

  #HEAD;
  #LAST;

  static #LINK = class {
    #RESOLVER;

    constructor(promise,resolver){
      this.promise = new Promise(resolve => ( this.#RESOLVER = resolve
                                            , promise.then(value => resolver({value, done: false}))
                                            ));
      this.next    = void 0;
    }

    get resolver(){
      return this.#RESOLVER;
    }
  }

  constructor(){
    new Promise(resolve => this.#HEAD = new AsyncQueue.#LINK(Promise.resolve(null),resolve));
  };

  enqueue(promise){
    this.#LAST = this.#LAST ? this.#LAST.next = new AsyncQueue.#LINK(promise,this.#LAST.resolver)
                            : this.#HEAD      = new AsyncQueue.#LINK(promise,this.#HEAD.resolver)
  }

  next(){
    var promise = this.#HEAD.promise.then(value => ({value, done: false}));
    this.#HEAD.next ? this.#HEAD = this.#HEAD.next
                    : this.#LAST = void 0;
    return promise;
  };

  [Symbol.asyncIterator]() {
    return this;
  };
};


var aq = new AsyncQueue();

async function getAsyncValues(){
  for await (let item of aq){
    console.log(`The Promise resolved with a value of ${item.value}`);
  };
};


getAsyncValues();
// synchronous insertion of promises
for (var i=1; i <= 5; i++) aq.enqueue(new Promise(resolve => setTimeout(resolve, 1000*i, `done at ${1000*i}ms`)));
// asynchronous insertion od a promise
setTimeout(aq.enqueue.bind(aq),7500,Promise.resolve("this is an asynchronnous insertion"));

\$\endgroup\$
1
  • \$\begingroup\$ I believe async function* serve({ hostname, port }) { while (true) yield { respond({ status, body }) { console.log(status, body); } }; } may also work. \$\endgroup\$ – tsh Mar 2 at 1:46

Your Answer

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

Browse other questions tagged or ask your own question.