How can I prevent the jupyter kernel from buffering stdout/stderr?

I am using an AsyncKernelManager + AsyncKernelClient to execute code in the jupyter kernel. I notice that even if code executes instantly, stdout/stderr messages both take much longer to appear on the iopub channel than the actual code executing.

Is the Jupyter kernel doing some sort of buffering internally?

The protocol doesn’t say the granularity of stream messages, it’s up to the kernel.
ipykernel at least defaults to buffering up to 200msec before flushing a stream message:

You can read/set sys.stdout.flush_interval and sys.stderr.flush_interval, even from a notebook, to tune this.
With a client, something like this probably works:

code='''
import sys
sys.stdout.flush_interval = 0.01
sys.stderr.flush_interval = 0.01
'''
client.execute_interactive(code=code)  # and optionally output_hook to suppress logging