porsager / postgres

Postgres.js - The Fastest full featured PostgreSQL client for Node.js, Deno, Bun and CloudFlare
The Unlicense
7.43k stars 271 forks source link

enchancement for listen #168

Closed mortifia closed 3 years ago

mortifia commented 3 years ago

Hello, are there any plans to add an Async Iterable to receive notify?

this would be rather interesting for the APIs using the websocket https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for-await...of#iterating_over_async_iterables

porsager commented 3 years ago

I can't really imagine the benefit. Could you try to provide a code sample of what you imagine?

Minigugus commented 3 years ago

If you really need an async iterable, you can write a simple polyfill, for instance:

const postgres = require('postgres');

const sql = postgres();

async function* subscribe(channel) {
  let
    done = false,
    availableLock = deferable(), // lock the generator function
    listener = await sql.listen(channel, value => provide(value));
  try {
    while (!done)
      yield await availableLock;
  } finally { // called on `break;` or exception (whenever the loop breaks)
    readyLock.resolve();
    listener.unlisten();
  }

  function provide(value) {
    availableLock.resolve(value);
    availableLock = deferable();
  }

  function deferable() {
    let resolve, reject;
    return Object.assign(
      new Promise((res, rej) => {
        resolve = res;
        reject = rej;
      }),
      { resolve, reject }
    )
  }
}

(async () => {
  setTimeout(() => sql.notify('test', 'Hello world!'), 200);
  for await (const value of subscribe('test')) {
    console.log(value);
    break;
  }
  sql.end();
})();

NOTE : This polyfill can drop some events if the loop block for too long.

mortifia commented 3 years ago

thanks @Minigugus

example is useful with websocket


  Mutation: {
    signUp: async (_source, _args, { dataSources }) => {
      return dataSources.pgDB.signUp(_args);
    },
    signIn: async (_source, _args, { dataSources }) => {
      return dataSources.pgDB.signIn(_args);
    },
    activateUser: async (_source, _args, { dataSources }) => {
      return dataSources.pgDB.activateUser(_args);
    },
    passwordForgot: async (_source, _args, { dataSources }) => {
      return dataSources.pgDB.passwordForgot(_args)
    },
    passwordReset: async (_source, _args, { dataSources }) => {
      return dataSources.pgDB.passwordReset(_args)
    },
    ticketUserCreate: async (_source, _args, { dataSources }) => {
      return dataSources.pgDB.ticketUserCreate(_args)
    },
    jwtRenew: async (_source, _args, { dataSources }) => {
      return dataSources.pgDB.jwtRenew(_args)
    },
  },

  TicketUserCreateOutput: {
    ticket: async (_source, _args, { dataSources }) => {
      return (await dataSources.pgDB.getTicketsByUserUuid(_source, _args))[0];
    },
  },

  Subscription: {
    user: {
      subscribe: (_source, _args) => {
        return pgDB.SubscriptionUser(_args); //Async Iterable here
      },
    }
  },
};```