Last active
February 5, 2026 05:05
-
-
Save hardikm9850/a63af8a86d74f1510be7033cf733af02 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <!DOCTYPE html> | |
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>SupervisorJob Exception Propagation</title> | |
| <style> | |
| body { | |
| font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; | |
| max-width: 1200px; | |
| margin: 0 auto; | |
| padding: 20px; | |
| background: #f5f5f5; | |
| } | |
| .container { | |
| background: white; | |
| border-radius: 12px; | |
| padding: 30px; | |
| margin-bottom: 30px; | |
| box-shadow: 0 2px 8px rgba(0,0,0,0.1); | |
| } | |
| h1 { | |
| color: #2c3e50; | |
| border-bottom: 3px solid #3498db; | |
| padding-bottom: 10px; | |
| } | |
| h2 { | |
| color: #34495e; | |
| margin-top: 30px; | |
| } | |
| .scenario { | |
| margin: 20px 0; | |
| } | |
| .diagram { | |
| display: flex; | |
| flex-direction: column; | |
| gap: 20px; | |
| margin: 20px 0; | |
| padding: 20px; | |
| background: #f8f9fa; | |
| border-radius: 8px; | |
| } | |
| .scope { | |
| border: 3px solid #9b59b6; | |
| border-radius: 8px; | |
| padding: 20px; | |
| background: #f4ecf7; | |
| position: relative; | |
| } | |
| .scope-label { | |
| position: absolute; | |
| top: -12px; | |
| left: 10px; | |
| background: #9b59b6; | |
| color: white; | |
| padding: 2px 10px; | |
| border-radius: 4px; | |
| font-size: 14px; | |
| font-weight: bold; | |
| } | |
| .children-container { | |
| display: flex; | |
| gap: 20px; | |
| margin-top: 10px; | |
| flex-wrap: wrap; | |
| } | |
| .coroutine { | |
| border: 2px solid #3498db; | |
| border-radius: 6px; | |
| padding: 15px; | |
| background: #ebf5fb; | |
| flex: 1; | |
| min-width: 200px; | |
| position: relative; | |
| } | |
| .coroutine.failed { | |
| border-color: #e74c3c; | |
| background: #fadbd8; | |
| } | |
| .coroutine.cancelled { | |
| border-color: #95a5a6; | |
| background: #ecf0f1; | |
| opacity: 0.7; | |
| } | |
| .coroutine.success { | |
| border-color: #27ae60; | |
| background: #d5f4e6; | |
| } | |
| .coroutine-label { | |
| font-weight: bold; | |
| margin-bottom: 8px; | |
| color: #2c3e50; | |
| } | |
| .coroutine.failed .coroutine-label { | |
| color: #c0392b; | |
| } | |
| .coroutine.cancelled .coroutine-label { | |
| color: #7f8c8d; | |
| } | |
| .coroutine.success .coroutine-label { | |
| color: #1e8449; | |
| } | |
| .nested { | |
| margin-top: 10px; | |
| padding: 10px; | |
| border-left: 3px solid #95a5a6; | |
| background: white; | |
| } | |
| .icon { | |
| display: inline-block; | |
| margin-right: 5px; | |
| } | |
| .explosion { | |
| font-size: 20px; | |
| } | |
| .check { | |
| color: #27ae60; | |
| font-size: 18px; | |
| } | |
| .cross { | |
| color: #e74c3c; | |
| font-size: 18px; | |
| } | |
| .arrow { | |
| color: #e74c3c; | |
| font-weight: bold; | |
| margin: 5px 0; | |
| } | |
| .legend { | |
| display: flex; | |
| gap: 20px; | |
| margin: 20px 0; | |
| flex-wrap: wrap; | |
| } | |
| .legend-item { | |
| display: flex; | |
| align-items: center; | |
| gap: 8px; | |
| } | |
| .legend-box { | |
| width: 40px; | |
| height: 30px; | |
| border-radius: 4px; | |
| border: 2px solid; | |
| } | |
| .barrier { | |
| border: 3px dashed #9b59b6; | |
| margin: 10px 0; | |
| position: relative; | |
| } | |
| .barrier-label { | |
| position: absolute; | |
| top: -12px; | |
| right: 10px; | |
| background: #9b59b6; | |
| color: white; | |
| padding: 2px 10px; | |
| border-radius: 4px; | |
| font-size: 12px; | |
| } | |
| .explanation { | |
| background: #fff9e6; | |
| border-left: 4px solid #f39c12; | |
| padding: 15px; | |
| margin: 15px 0; | |
| border-radius: 4px; | |
| } | |
| .code-ref { | |
| background: #ecf0f1; | |
| padding: 2px 6px; | |
| border-radius: 3px; | |
| font-family: monospace; | |
| font-size: 14px; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="container"> | |
| <h1>π SupervisorJob Exception Propagation</h1> | |
| <div class="legend"> | |
| <div class="legend-item"> | |
| <div class="legend-box" style="border-color: #e74c3c; background: #fadbd8;"></div> | |
| <span>Failed (threw exception)</span> | |
| </div> | |
| <div class="legend-item"> | |
| <div class="legend-box" style="border-color: #95a5a6; background: #ecf0f1;"></div> | |
| <span>Cancelled</span> | |
| </div> | |
| <div class="legend-item"> | |
| <div class="legend-box" style="border-color: #27ae60; background: #d5f4e6;"></div> | |
| <span>Completed successfully</span> | |
| </div> | |
| <div class="legend-item"> | |
| <div class="legend-box" style="border-color: #9b59b6; background: #f4ecf7;"></div> | |
| <span>SupervisorJob scope</span> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="container"> | |
| <h2>β Scenario 1: Direct Children (Exception Contained)</h2> | |
| <p>When coroutines are <strong>direct children</strong> of a SupervisorJob scope, they are isolated from each other.</p> | |
| <div class="diagram"> | |
| <div class="scope"> | |
| <div class="scope-label">CoroutineScope(SupervisorJob())</div> | |
| <div class="barrier"> | |
| <div class="barrier-label">π‘οΈ Protection Barrier</div> | |
| </div> | |
| <div class="children-container"> | |
| <div class="coroutine failed"> | |
| <div class="coroutine-label"><span class="explosion">π₯</span> launch { ... }</div> | |
| <div>Direct Child 1</div> | |
| <div class="arrow">β Exception thrown</div> | |
| <div style="color: #c0392b; font-weight: bold;">β This child fails</div> | |
| </div> | |
| <div class="coroutine success"> | |
| <div class="coroutine-label"><span class="check">β</span> launch { ... }</div> | |
| <div>Direct Child 2</div> | |
| <div style="margin-top: 10px;">π‘οΈ Protected by SupervisorJob</div> | |
| <div style="color: #1e8449; font-weight: bold;">β Continues running!</div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="explanation"> | |
| <strong>π‘ Key Point:</strong> The exception in Child 1 does NOT propagate to Child 2 because the SupervisorJob creates a protective barrier between direct siblings. | |
| </div> | |
| <pre style="background: #2c3e50; color: #ecf0f1; padding: 15px; border-radius: 6px; overflow-x: auto;"><code>val scope = CoroutineScope(SupervisorJob()) | |
| scope.launch { throw Exception("Boom") } // β Direct child | |
| scope.launch { println("I survive!") } // β Direct child (protected)</code></pre> | |
| </div> | |
| <div class="container"> | |
| <h2>β Scenario 2: Nested Children (Exception Propagates)</h2> | |
| <p>When coroutines are <strong>nested</strong> (children of children), SupervisorJob does NOT protect them.</p> | |
| <div class="diagram"> | |
| <div class="scope"> | |
| <div class="scope-label">CoroutineScope(SupervisorJob())</div> | |
| <div class="barrier"> | |
| <div class="barrier-label">π‘οΈ Barrier only at this level</div> | |
| </div> | |
| <div class="children-container"> | |
| <div class="coroutine cancelled" style="flex: 1; min-width: 400px;"> | |
| <div class="coroutine-label">launch { ... } - Parent Coroutine</div> | |
| <div style="margin-bottom: 10px;">Direct Child (gets cancelled when child fails)</div> | |
| <div style="border-top: 2px dashed #95a5a6; padding-top: 10px; margin-top: 10px;"> | |
| <div style="font-weight: bold; margin-bottom: 10px; color: #7f8c8d;">Nested children (NO protection):</div> | |
| <div class="nested"> | |
| <div class="coroutine failed" style="margin-bottom: 10px;"> | |
| <div class="coroutine-label"><span class="explosion">π₯</span> async { task1() }</div> | |
| <div class="arrow">β Exception thrown</div> | |
| <div class="arrow">β Propagates to parent launch</div> | |
| <div style="color: #c0392b; font-weight: bold;">β Fails</div> | |
| </div> | |
| <div class="coroutine cancelled"> | |
| <div class="coroutine-label"><span class="cross">β</span> async { task2() }</div> | |
| <div class="arrow">β Parent gets cancelled</div> | |
| <div style="color: #7f8c8d; font-weight: bold;">β οΈ Gets cancelled too!</div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="explanation"> | |
| <strong>π‘ Key Point:</strong> SupervisorJob only protects <span class="code-ref">direct children</span>. When task1() fails, it cancels its parent <span class="code-ref">launch</span>, which then cancels task2(). The barrier doesn't extend to nested levels! | |
| </div> | |
| <pre style="background: #2c3e50; color: #ecf0f1; padding: 15px; border-radius: 6px; overflow-x: auto;"><code>val scope = CoroutineScope(SupervisorJob()) | |
| scope.launch { // β Direct child | |
| val t1 = async { throw Exception() } // β Nested (NOT protected) | |
| val t2 = async { ... } // β Nested (gets cancelled) | |
| t1.await() // Exception propagates up to launch | |
| }</code></pre> | |
| </div> | |
| <div class="container"> | |
| <h2>β Scenario 3: Your Original Code FIXED</h2> | |
| <p>Using <span class="code-ref">supervisorScope</span> creates a new protection barrier at the nested level.</p> | |
| <div class="diagram"> | |
| <div class="scope"> | |
| <div class="scope-label">CoroutineScope(SupervisorJob())</div> | |
| <div class="barrier"> | |
| <div class="barrier-label">π‘οΈ Outer Barrier</div> | |
| </div> | |
| <div class="children-container"> | |
| <div class="coroutine success" style="flex: 1; min-width: 400px;"> | |
| <div class="coroutine-label">launch { ... } - Parent</div> | |
| <div style="margin-bottom: 10px;">Direct Child</div> | |
| <div style="border: 3px solid #9b59b6; border-radius: 6px; padding: 10px; margin-top: 10px; background: #f4ecf7;"> | |
| <div style="font-weight: bold; margin-bottom: 10px; color: #8e44ad;">supervisorScope { ... }</div> | |
| <div class="barrier" style="border-color: #8e44ad;"> | |
| <div class="barrier-label" style="background: #8e44ad;">π‘οΈ Inner Barrier</div> | |
| </div> | |
| <div style="display: flex; gap: 10px; margin-top: 15px;"> | |
| <div class="coroutine failed" style="flex: 1;"> | |
| <div class="coroutine-label"><span class="explosion">π₯</span> async { task1() }</div> | |
| <div style="color: #c0392b; font-weight: bold; margin-top: 5px;">β Fails</div> | |
| <div style="font-size: 12px; color: #7f8c8d;">Isolated by supervisorScope</div> | |
| </div> | |
| <div class="coroutine success" style="flex: 1;"> | |
| <div class="coroutine-label"><span class="check">β</span> async { task2() }</div> | |
| <div style="color: #1e8449; font-weight: bold; margin-top: 5px;">β Survives!</div> | |
| <div style="font-size: 12px; color: #1e8449;">Protected by barrier</div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="explanation"> | |
| <strong>π‘ Solution:</strong> By wrapping the <span class="code-ref">async</span> calls in a <span class="code-ref">supervisorScope</span>, you create a new protection barrier. Now task1 and task2 are siblings at the same level, so they don't cancel each other! | |
| </div> | |
| <pre style="background: #2c3e50; color: #ecf0f1; padding: 15px; border-radius: 6px; overflow-x: auto;"><code>scope.launch { | |
| supervisorScope { // β Creates new barrier here! | |
| val t1 = async { throw Exception() } // Protected | |
| val t2 = async { ... } // Protected | |
| val r1 = runCatching { t1.await() } | |
| val r2 = runCatching { t2.await() } // Still completes! | |
| } | |
| }</code></pre> | |
| </div> | |
| <div class="container"> | |
| <h2>π Summary: Exception Propagation Rules</h2> | |
| <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 20px; margin-top: 20px;"> | |
| <div style="border: 2px solid #3498db; padding: 20px; border-radius: 8px; background: #ebf5fb;"> | |
| <h3 style="margin-top: 0; color: #2980b9;">π‘οΈ SupervisorJob</h3> | |
| <ul style="line-height: 1.8;"> | |
| <li>Only protects <strong>direct children</strong></li> | |
| <li>Siblings don't cancel each other</li> | |
| <li>Exceptions still propagate upward</li> | |
| <li>One level deep only</li> | |
| </ul> | |
| </div> | |
| <div style="border: 2px solid #9b59b6; padding: 20px; border-radius: 8px; background: #f4ecf7;"> | |
| <h3 style="margin-top: 0; color: #8e44ad;">π supervisorScope</h3> | |
| <ul style="line-height: 1.8;"> | |
| <li>Creates barrier at any nesting level</li> | |
| <li>Use inside coroutines</li> | |
| <li>Protects sibling coroutines</li> | |
| <li>Suspending function</li> | |
| </ul> | |
| </div> | |
| <div style="border: 2px solid #e67e22; padding: 20px; border-radius: 8px; background: #fef5e7;"> | |
| <h3 style="margin-top: 0; color: #d35400;">π― Best Practice</h3> | |
| <ul style="line-height: 1.8;"> | |
| <li>Use <code style="background: white; padding: 2px 6px; border-radius: 3px;">runCatching</code> with await()</li> | |
| <li>Handle failures explicitly</li> | |
| <li>Don't rely on implicit supervision</li> | |
| <li>Be clear about error handling</li> | |
| </ul> | |
| </div> | |
| </div> | |
| </div> | |
| </body> | |
| </html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment