Closed thejmazz closed 7 years ago
Summary and notes from chat about this:
prefinish
event to cork and uncork writable stream just before it emits finish
: so for example, catch prefinish
from a writable stream, resolve and validate output, then uncork and the wrapping duplex emits finish
: prefinish example
end
after the child readable does and output resolution/validation is donetask
returning a stream object:
end
if it never has a buffer, so need to call .resume()
to "make it alive" so that then it can emit end
right afterend
not close
: close
is meant for errors!resume()
or pipe
or .on('data')
instead
myTask().stream.pipe(hackInnerStreamIntoWritable)
(not recommended)myTask()
try to avoid "foot shooting" - example, if a streaming task is joined to a non-writable:
join(streamingTask, somethingThatIsNotWritable)
done with async-done
Consider this task:
The
operation
for this task will return a writable stream. Thendup.setWritable(operation)
will apply it to the task duplexify stream. Similarly for readable.Then in the catchTask action, the purpose is to resolve once the operation has completed. It's called "catch" because it stops the task lifecycle action pipeline until the operation is completed, so that then resolve output and validate output can be ran.
After output is successfully validated (file is not empty, etc.), I set an
_output
object in the duplex stream, and destroy the stream:On
destroy()
the duplex stream emits aclose
. Then the lifecycle of a task is something like:and
join
will use that to run tasks after each other, collect all the outputs into the "output dump", etc.However, this breaks if the
operator
itself throws aclose
event, as what happens with child processes. My solution to this was (which only switched to theemit('destroy')
hack recently as I was hacking features into refactored code) was to add a function to the stream object, which could access things through a closure, and would run the output resolution and validations inside that function. On any "ending type" event - end, close, finish, you could call it. So the lifecycle for a task was:Which is better because it uses no made up events, but a little annoying because if you just do
if will not actually run the whole task lifecycle until
this._output()
is called. Perhaps ataskWrapper
could auto call this, and then itself emit aclose
.Ideally:
This way task lifecycle is contained in one place without doing weird wrappers that would themselves need to emit
close
, and the resolved output is available immediately when a task is "done".I saw mention of a flush function, but perhaps only for a writable stream?
Also,
operations
which are actually just duplex streams, like a transform, can be set as the duplex insidetask
and forking/parallel should work as iftask
is a regularduplex
; so the "catch finish" need not apply since no output files need to resolved to absolute paths.