The :class:`~lightning_app.core.app.LightningApp` runs a tree of one or more components that interact to create end-to-end applications. There are two kinds of components: :class:`~lightning_app.core.flow.LightningFlow` and :class:`~lightning_app.core.work.LightningWork`. This modular design enables you to reuse components created by other users.
Lightning Work
^^^^^^^^^^^^^^
The :class:`~lightning_app.core.work.LightningWork` component is a building block optimized for long-running jobs or integrating third-party services. LightningWork can be used for training large models, downloading a dataset, or any long-lasting operation.
Lightning Flow
^^^^^^^^^^^^^^
The :class:`~lightning_app.core.flow.LightningFlow` component coordinates long-running tasks :class:`~lightning_app.core.work.LightningWork` and runs its children :class:`~lightning_app.core.flow.LightningFlow` components.
Lightning App Tree
^^^^^^^^^^^^^^^^^^
Components can be nested to form component trees where the LightningFlows are its branches and LightningWorks are its leaves.
Here's a basic application with four flows and two works:
A Lightning App runs all flows into a single process. Its flows coordinate the execution of the works each running in their own independent processes.
Lightning Distributed Event Loop
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Drawing inspiration from modern web frameworks like `React.js <https://reactjs.org/>`_, the Lightning app runs all flows in an **event loop** (forever), which is triggered every 0.1 seconds after collecting any works' state change.
When running an app in the cloud, the :class:`~lightning_app.core.work.LightningWork` run on different machines. Lightning communicates any :class:`~lightning_app.core.work.LightningWork` state changes to the **event loop** which re-executes the flow with the newly-collected works' state.
Lightning App State
^^^^^^^^^^^^^^^^^^^
By design, each component is stateful and its state is composed of all its attributes. The **Lightning App State** is the collection of all its components state.
With this mechanism, any component can **react** to any other component **state changes**, simply by relying on its attributes within the flow.
For example, here we define two flow components, **RootFlow** and **ChildFlow**, where the child flow prints and increments a counter indefinitely and gets reflected in **RootFlow** state.
You can easily check the state of your entire app:
With Lightning, you can control how to run your components.
By default, the :class:`~lightning_app.core.flow.LightningFlow` is executed infinitely by the **Lightning Infinite Loop** and the :class:`~lightning_app.core.work.LightningWork` does not run in **parallel**,
meaning the **Lightning Infinite Loop** (a.k.a the flow) waits until that long-running work is completed to continue.
Similar to `React.js Components and Props <https://reactjs.org/docs/components-and-props.html>`_, the :class:`~lightning_app.core.work.LightningWork`
component accepts arbitrary inputs (the "props") to its **run** method and by default runs **once** for each unique input provided.
INFO: Your app has started. View it in your browser: http://127.0.0.1:7501/view
# After you have clicked `run` on the UI.
I received the following props: args: () kwargs: {'value': 1}
I received the following props: args: () kwargs: {'value': 1}
I received the following props: args: () kwargs: {'value': 1}
I received the following props: args: () kwargs: {'value': 1}
I received the following props: args: () kwargs: {'value': 1}
I received the following props: args: () kwargs: {'value': 10}
..note:: Passing a sequence of different props to the work run method queues their execution. We recommend avoiding this behavior as it can be hard to debug. Instead, wait for the previous run to execute.
LightningWork: Parallel vs Non Parallel
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The LightningWork component is made for long-running jobs.
As an example, let's create a long-running **LightningWork** component that will take 1 hour to do its "work".