1

We want to consume stream data events from a generator into a transformation/transduction pipeline.

One form of a generators is the following

var generator = async function* () {
    var value
    while(true){
        value = await new Promise((resolve)=>{
                value = ... // some functionality
                resolve(value)
        })
        yield value
    }
}

Assume there is a data stream that is producing values. There is a factory function available that takes a handler function

async function makeStream(handler) {
    ...
}

The handle function will provide a payload representing a value produced from the stream.

var handler = function(payload){
}

For the stream we need to provide a callback. For the generator we need to resolve the promise when the handler is invoked. We want to be able to write code something like the following:

function makeCallbackGenerator() {
    var handler
    var generator = async function*() {
        while(true){
            var value = await new Promise((resolve)=>{
                handler = function(payload){
                    resolve(payload)
                }
            })
            yield value
        }
    }
    return {
        handler,
        generator
    }
}

This is a factory function which produces

  • a generator
  • a callback

The callback is needed to be passed into the stream. The generator is required to pass into the transformation pipeline.

But this declaration isnt right. We dont want to define the function on each iteration of the promise, instead we want to use the function to resolve the promise on each iteration.

The fundamental problem is how to define the callback within the promise so that the stream can be coupled to the generator.

A work around is to use a buffer between the stream and the generator.

function makeCallbackGenerator() {
    var buffer = []
    var handler = function(payload){
        buffer.push(payload)
        return 1//consumed
    }
    var start = async function* () {
        while(true){
            if(buffer.length>0){
                var next = buffer.pop()
                debug("Generator yield")
                yield next
            }else {
                await new Promise((resolve)=>{
                    setTimeout(()=>resolve(),1000)
                })
            }
        }
    }
    return {
        handler, start
    }
}

Is there a simpler way to achieve this without a buffer?

  • Thank you for the question, this is very beautiful solution for async stream generator. But I do not see why would you want this to be simpler. It's already a simple solution of a complex problem. I am trying to understand what do you expect to achieve here. Again, great question! – pegasuspect Jul 24 '19 at 00:09
  • It would be nice to eliminate the buffer which is problematic in that it might be a memory leak and it makes the code more complex. Fundamentally I just want to link the incoming callback method with a generator...so that it can feed a transduction pipeline (https://www.npmjs.com/package/funprog) – Phil Tomlinson Jul 27 '19 at 11:33
  • You can use mutex and lock the buffer to eliminate any race conditions, also in the link you provided, similar to your example code; it uses MongoDB, just like a buffer. So why not use buffer? – pegasuspect Jul 28 '19 at 19:38

1 Answers1

0

I had the same question it is possible to simplify a solution without a buffer and generator. 

I finally found for me much better solution with async function in Node-Media-Server project, async branch.

In v1.2.7 is with generator function.

I tested performance of the RTSP server that I build with both provided async and generator function solutions and got the same results. As I remember several Gbits/sec and 1000 concurrent clients per cpu core.

Yunus Temurlenk
  • 4,085
  • 4
  • 18
  • 39
goxy
  • 1
  • 1