In Javascript / ES6, how do I wait for Python code to finish executing in a Jupyter Notebook?
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
If JavaScript needs to wait for Python running in a Jupyter kernel, the right model is asynchronous request tracking, not sleeping for an arbitrary amount of time. You send code to the kernel, keep the execution future or callback, and resolve a Promise when the kernel reports that execution finished.
The Core Idea: Wrap Kernel Execution in a Promise
In modern Jupyter front-end code, kernel execution is asynchronous. That means the JavaScript side should expose an async function and await it.
A JupyterLab-style example looks like this:
The important part is await future.done. That is the moment JavaScript actually waits for Python execution to finish.
Why setTimeout Is the Wrong Tool
A common bad pattern is:
This is unreliable because kernel execution time depends on:
- notebook load
- current kernel state
- data size
- machine performance
- remote latency
If Python finishes sooner, you waited too long. If Python finishes later, your JavaScript races ahead anyway.
Promises and kernel futures solve the real synchronization problem instead of guessing.
Handling Errors Explicitly
You usually want the Promise to reject if the kernel execution fails:
A more complete version would capture the error and reject a Promise cleanly, but the key idea is that kernel status should drive the control flow.
This matters a lot in notebooks because silent failures are common if you only watch output areas and never check the execution reply.
Legacy Notebook Pattern
Older classic Notebook front-ends often used globals such as Jupyter.notebook.kernel.execute. The same async principle still applies: wrap the completion callback in a Promise and resolve it when the kernel replies.
Conceptually, it looks like this:
Then:
The exact API surface depends on the notebook front-end, but the design pattern is the same in both classic and modern environments.
Keep JavaScript and Python Responsibilities Clear
A lot of notebook code becomes brittle because both languages try to manage the same workflow state. It is usually cleaner to let:
- Python do the heavy computation
- JavaScript handle UI behavior and await kernel completion
That separation makes async coordination much easier than bouncing state back and forth through notebook output cells.
Returning Data Instead of Just Waiting
Waiting is only half the story. Most real use cases also need the result. In modern Jupyter front-end APIs, the execution future lets you collect stream messages, rich display data, and execute results while still awaiting completion.
That means one Promise can do all three jobs:
- start Python execution
- gather outputs
- resolve only when the kernel is finished
Once you think of the kernel request as an async task with a completion handle, the JavaScript side becomes straightforward.
Common Pitfalls
- Using
setTimeoutto guess when Python finished. - Starting a kernel execution and ignoring the completion future or reply callback.
- Treating notebook output rendering as proof that execution fully completed.
- Mixing classic notebook globals with modern JupyterLab APIs without checking which front-end you are actually running.
- Ignoring kernel error replies and then wondering why later JavaScript state is wrong.
Summary
- JavaScript should wait for Python in Jupyter by awaiting the kernel execution future, not by sleeping.
- In modern front-ends,
requestExecuteplusawait future.doneis the core pattern. - In classic notebook code, wrap the execution callback in a Promise and await that Promise.
- The same async model can both wait for completion and collect outputs.
- Reliable notebook integration comes from explicit kernel completion handling, not time-based guesses.

