<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Volodymyr Potiichuk</title>
    <description>The latest articles on DEV Community by Volodymyr Potiichuk (@volodymyr_potiichuk).</description>
    <link>https://dev.to/volodymyr_potiichuk</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2487227%2F5c8873d6-1113-42f0-8f00-52ca89ceb051.jpg</url>
      <title>DEV Community: Volodymyr Potiichuk</title>
      <link>https://dev.to/volodymyr_potiichuk</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/volodymyr_potiichuk"/>
    <language>en</language>
    <item>
      <title>Creation of a job processing library using Elixir and Redis</title>
      <dc:creator>Volodymyr Potiichuk</dc:creator>
      <pubDate>Sat, 25 Jan 2025 17:28:33 +0000</pubDate>
      <link>https://dev.to/volodymyr_potiichuk/creation-of-a-job-processing-library-using-elixir-and-redis-2o8j</link>
      <guid>https://dev.to/volodymyr_potiichuk/creation-of-a-job-processing-library-using-elixir-and-redis-2o8j</guid>
      <description>&lt;p&gt;Today I will cover how you can build your job processing library and understand the core aspects of creating such a tool. This article adds to the previous one about &lt;a href="https://dev.to/volodymyr_potiichuk/how-to-use-queue-data-structure-in-programming-5h7"&gt;queue data structure&lt;/a&gt;. I want to demonstrate a real-world example of using queues and build a job processing library that others can use in their projects.&lt;/p&gt;

&lt;h3&gt;
  
  
  A tool that we are going to build
&lt;/h3&gt;

&lt;p&gt;Today we are going to build a library that will be able to execute tasks from a queue by a separate process (worker), store these tasks persistently in Redis, and have the ability to check statistics of processed, failed, and scheduled tasks.&lt;/p&gt;

&lt;p&gt;To develop this library, we need to familiarize ourselves with the terminology:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Job&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Simply put, a job is any function we put in the queue and want to execute.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Worker&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A worker is an independent process that performs jobs that are queued. Usually, there can be several workers to process tasks faster, but for simplicity of the code we will create functionality for only one worker, but it will be more than enough.&lt;/p&gt;

&lt;h3&gt;
  
  
  Prerequisites
&lt;/h3&gt;

&lt;p&gt;There are several prerequisites for building such a tool, and they’re chosen for a reason, let’s look at why:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://redis.io/docs/latest/develop/get-started/" rel="noopener noreferrer"&gt;&lt;strong&gt;Redis&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Redis is an open-source, in-memory application that we will use as a database for data persistence. The main advantage of Redis is the speed of executing operations because it works straight in RAM (Random Access Memory) and you don’t need to write and read a disk whenever you need some data.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://elixir-lang.org/" rel="noopener noreferrer"&gt;&lt;strong&gt;Elixir&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Elixir is a dynamic, scalable, functional programming language that is especially good for such applications due to its fault-tolerant behavior.&lt;/p&gt;

&lt;p&gt;Also, some Elixir libraries will help us on our way:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/whatyouhide/redix" rel="noopener noreferrer"&gt;&lt;strong&gt;Redix&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Simple client for Redis, to have the ability to write Redis requests in Elixir language.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/michalmuskala/jason" rel="noopener noreferrer"&gt;&lt;strong&gt;Jason&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Jason is a library that allows you to encode and parse a JSON structure.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://hexdocs.pm/uuid/readme.html" rel="noopener noreferrer"&gt;&lt;strong&gt;UUID&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A library that will allow us to generate unique IDs for our tasks so that we can distinguish them by a unique identifier.&lt;/p&gt;

&lt;h3&gt;
  
  
  Project structure
&lt;/h3&gt;

&lt;p&gt;To build such a library, let’s understand what basic modules we need for our application to work and I assume that we need to create the following ones:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;em&gt;Enqueuer&lt;/em&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This module will be responsible for putting the jobs into the queue.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;em&gt;Dequeuer&lt;/em&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This module will be responsible for taking jobs from the queue.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;em&gt;Job&lt;/em&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The module is responsible for serialization, deserialization, and data preparation for the queue.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;em&gt;Stats&lt;/em&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This module will be able to show us statistics on processed, failed, and scheduled tasks.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;em&gt;QueueWorker&lt;/em&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This module is responsible for processing jobs from the queues.&lt;/p&gt;

&lt;h3&gt;
  
  
  Flow
&lt;/h3&gt;

&lt;p&gt;Before we start implementing this via code, I would like to show how our application is supposed to work with two flow diagrams:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx3e89ytdhnmpuhv9pguh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx3e89ytdhnmpuhv9pguh.png" alt="Client flow diagram" width="800" height="240"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here is how our library will work when the client creates a new task:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;The client provides raw data about the task to the Enqueuer module.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The Enqueuer module asks the Job module to construct job data and serialize it to JSON.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The Enqueuer module passes the generated JSON to the Queue module.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The Queue module saves JSON in the database using the Database module.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We send an asynchronous message to the worker that we have a task in this queue, and if the worker is up and available, he can process it.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Return the created job to the client.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;And the second one from the queue worker perspective:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1p45t5jqq25wka3xk0r5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1p45t5jqq25wka3xk0r5.png" alt="Queue worker flow diagram" width="800" height="975"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Queue worker checks all queues under the library namespace in Redis.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Queue worker takes the first queue he finds and peeks elements from the start of the queue (FIFO principle).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Queue worker parses the job from JSON to struct via the Job module&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Queue worker starts to process the parsed job&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;When the job is completed, the queue worker moves the job to a different queue based on the result, either failed or processed.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Recursively repeat from the second point until the queue is empty, after that we can go to the next queue.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;After all tasks are completed, the worker process remains in standby mode, and as soon as a new task appears, it will be ready to process it. If the worker is not enabled at the moment of task enqueue, all such tasks will be processed at the moment of worker startup.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Project initialization
&lt;/h3&gt;

&lt;p&gt;Let’s start with the creation of the project. We will use the &lt;a href="https://hexdocs.pm/elixir/introduction-to-mix.html" rel="noopener noreferrer"&gt;Mix&lt;/a&gt; tool, which shipped with Elixir, and use the “mix new ” command for project initiation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mix new processing_library
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;After command execution, we should get a ready-made set for developing our projects, namely:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;folders structure&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;dependency management&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;building tooling&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;test environment&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;and in my case, there is such structure with such file naming:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqngs9ni91zix418cevgu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqngs9ni91zix418cevgu.png" alt="“mix new &amp;lt;project_name” command result" width="800" height="643"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After successful project initiation, we can add the dependencies to the “mix.exs” file:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;

&lt;p&gt;and install them as well with the Mix tool:&lt;br&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mix deps.get
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Also, we need to start Redis, I usually do it through the docker engine and docker-compose utility. Still, you can do it through any convenient installer like homebrew or asdf. Anyway, here’s my docker-compose.yml file:&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;

&lt;p&gt;This configuration will start Redis on Linux Alpine inside the container on port 6379 and we will redirect it to our local host on port 6379. To run it, you need to start the container via docker-compose CLI:&lt;br&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker-compose up redis
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;And if everything goes well, you will face something like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu0vbb6562kn6owng9xq2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu0vbb6562kn6owng9xq2.png" alt="Successful project bootstrap" width="800" height="119"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Implementation
&lt;/h3&gt;

&lt;p&gt;I suggest we start our journey with the module responsible for the tasks:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;

&lt;p&gt;There, we define the job structure and functions accountable for serialization, deserialization, and job creation from raw data. The structure of the job in our case will have the following fields:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;params&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are variables that will be passed to the job function as parameters:&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;&lt;em&gt;worker_module&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is an abstract module responsible for executing a particular task, and every module that inherits it through &lt;a href="https://elixirschool.com/en/lessons/advanced/behaviours" rel="noopener noreferrer"&gt;behavior&lt;/a&gt; directive must implement the “perform” function that the queue worker will eventually call:&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;&lt;em&gt;jid (Job ID)&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For each task, we will assign a job ID to be able to distinguish them by a unique identifier.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;error&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Since a job can also break during execution, saving the error in the job object would be a good idea.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;start_at, finish_at&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These time stamps will indicate how long it took the worker to complete the task.&lt;/p&gt;

&lt;p&gt;Next, we want to create a module that is a simple command wrapper around the Redix library. This library does not provide high-level functions, only &lt;a href="https://hexdocs.pm/redix/Redix.html#command/3" rel="noopener noreferrer"&gt;Redix.command/3&lt;/a&gt;, an alternative to writing commands directly in the redis-cli client. For a better understanding of what we want to do, let’s show an example of viewing an item in a Redis queue:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
Instead, we will have a function in the Redis module that will be responsible for peeking elements:&lt;br&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
This approach will help us to divide the area of responsibility between files and increase the cohesion of the modules. Good, now let’s take a look at the elements that make up our Redis wrapper module:&lt;br&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
Here we use &lt;a href="https://hexdocs.pm/elixir/GenServer.html" rel="noopener noreferrer"&gt;GenServer behavior&lt;/a&gt; to maintain a single Redis connection and to be able to manage this process through the Elixir supervisor. For the client to customize where the library should look for the Redis process, we provide the ability to configure this data via config settings:&lt;br&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
In the case of our library, we have several variables and the most interesting one is “redis_namespace”, it allows us to distinguish between library redis data and others. For this purpose, we will have special functions that wrap and unwrap Redis keys in the provided library namespace:&lt;br&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
Using these methods we will create a set of core functions, which we will use in the future (P.S. There is only the public API for simplicity, but if you want to see the full file, click &lt;a href="https://github.com/ExuCounter/processing-library/blob/main/lib/redis.ex" rel="noopener noreferrer"&gt;here&lt;/a&gt;):


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Next, with our Redis module, we can create an abstraction module for the database:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;We are simply delegating our method calls from the database to the Redis module, but why? Why not just call methods from the Redis module directly?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Explanation:&lt;/strong&gt; Imagine you’re writing a program and you’re relying on the interface Redis provides. At some moment you realize that you need to switch from Redis to PostgreSQL. You start looking at the code and realize that every part of your program depends not on an abstract database, but on Redis, which makes the move very difficult. That’s why we want to hide the implementation details behind a certain interface so that in case of anything, we would have to change only one module instead of the whole program.&lt;/p&gt;

&lt;p&gt;The same goes for the queue module:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
The output is a simple and clear interface that can be interacted with.

&lt;p&gt;Having an abstraction over the database and the queue, we can start working with the modules that work most closely with the queue, namely the enqueuer and dequeuer modules:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;In this module:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;We create a job from incoming parameters&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Add it to the Redis queue&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If the queue to which we add is not a queue reserved by our library (like processed or failed queues), we send the asynchronous message to the Queue Worker and if it is available it will process it.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Something similar is happening with the dequeuer module:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
The remove/1 function is responsible for pulling an item from the queue without paying attention to the order using the find_job/1 function, it is very convenient if we want to remove a task from the queue that was already scheduled some time ago. The last dequeue/1 function is passed directly to the Queue module since we are completely satisfied with it.

&lt;p&gt;Now we going to the fun part, we’ll move on to the queue worker module:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
At the very beginning, we request all queues available under our library’s namespace and send them for processing:&lt;br&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
After that, going through each queue, we synchronously look at the first available task (by FIFO principle), and send it for processing:&lt;br&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
At the moment of job processing:

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;We save timestamps and log information about the job start to the I/O.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Execute job through &lt;a href="https://hexdocs.pm/elixir/1.12.3/Kernel.html#apply/3" rel="noopener noreferrer"&gt;Kernel.apply/3&lt;/a&gt; function&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Save the execution result in one of the queues (either failed or processed).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Start executing the next task in the queue recursively.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That’s all the queue worker does, but it also accepts other signals from outside (e.g. from the Enqueuer module), and can perform such tasks asynchronously thanks to &lt;a href="https://hexdocs.pm/elixir/1.13/GenServer.html#cast/2" rel="noopener noreferrer"&gt;GenServer.cast/2:&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
Great, now let’s connect our queue worker so it starts when the client application is started. It’s important to clarify that we will disable the worker and database in the test environment so it can be done manually before running the tests:&lt;br&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;After adding our processes to the supervisor, we can start our library and start using it. But wait, something is missing… oh… statistics module. Let’s implement it very fast:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Inside this module we check the queues, pick the number of jobs inside them, and provide a map as a result:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;%{processed: 105, scheduled: 70, failed: 5}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Finally, the cherry on top will be a public interface for users, which will provide everything users need and remove the necessity to go deep into modules using our library:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;h3&gt;
  
  
  Demo
&lt;/h3&gt;

&lt;p&gt;I made a dashboard using &lt;a href="https://hexdocs.pm/phoenix_live_view/Phoenix.LiveView.html" rel="noopener noreferrer"&gt;Phoenix LiveView&lt;/a&gt; and WebSockets to demonstrate how our library works visually. Also, for this purpose, I added a dummy worker module to our project, which will randomly set a delay for our task between 1 and 3 seconds and randomly throw an error as well so that we could see such cases in UI:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;in the demo itself, I’ll enqueue 10 real-life tasks with our dummy worker, and let’s see how the queue worker handles them:&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/cOq9JFaS2mM"&gt;
&lt;/iframe&gt;
This demonstration shows how the queue worker pulls up one scheduled task each time, synchronously executes it, moves it to one of the queues depending on the result, and proceeds to the next one.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;At the end of the story, I want to say that it was super exciting to write such a library, I learned and understood a lot of things related to the concept of job processing tools. I’ll leave a link to the repository &lt;a href="https://github.com/ExuCounter/processing-library" rel="noopener noreferrer"&gt;here&lt;/a&gt; in case anyone wants to check out the whole codebase. Thanks for reading, I hope you have learned something new too!&lt;/p&gt;

</description>
      <category>elixir</category>
      <category>redis</category>
      <category>library</category>
      <category>worker</category>
    </item>
    <item>
      <title>How to use queue data structure in programming</title>
      <dc:creator>Volodymyr Potiichuk</dc:creator>
      <pubDate>Fri, 03 Jan 2025 10:54:45 +0000</pubDate>
      <link>https://dev.to/volodymyr_potiichuk/how-to-use-queue-data-structure-in-programming-5h7</link>
      <guid>https://dev.to/volodymyr_potiichuk/how-to-use-queue-data-structure-in-programming-5h7</guid>
      <description>&lt;p&gt;A queue in classic programming is a &lt;strong&gt;data structure (DS)&lt;/strong&gt; used for storing and managing data in a specific order. In the real-world simulation, you can imagine a queue of people waiting for their order, and people who ordered it first will receive it first and vice-versa.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdiyyb33blv8ve4rm2rda.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdiyyb33blv8ve4rm2rda.png" alt="Preview" width="800" height="390"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This concept is used widely for different reasons in programming and the most popular are those:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Task scheduling&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In distributed computing, queues contain a list of tasks to be executed by a process (or several processes, also called workers). When a task is created, it is added to the end of the queue, and the task waits for a free worker to pick it up. Queues in task scheduling can be different, and they don’t always follow the FIFO principle. It can also be:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Priority queues (Each task has its priority, typically a plain number)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Delayed queues (Each task has its time of execution, for example, scheduled email letters)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Dead letter queues (Tasks that worker can’t process, remain for further investigation)&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Real-time event processing (Message brokers)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Queues in event-driven architecture are used as a core component. It allows asynchronous communication between the components of the systems (producer and consumers). The producer adds a message to the broker’s queue, and then as soon as a consumer is available, it will pull messages out.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Any other system&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That requires the creation of a real queue simulation, like a call center or online game lobby.&lt;/p&gt;

&lt;h3&gt;
  
  
  Interface
&lt;/h3&gt;

&lt;p&gt;At the previous stage, we figured out where the queue is being used, and it would be cool to understand what contract this data structure has.&lt;/p&gt;

&lt;p&gt;There are several common methods of this DS:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;enqueue&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This core method is responsible for adding the item to the queue.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;dequeue&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This method is responsible for pulling the element from the queue according to the principle that the queue follows.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;peek&lt;/em&gt; (front or rear)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This method allows us to view the item from the rear or front without pulling it out. This can be useful if we accidentally pull an item and our process that handles it falls, in which case a pulled item may be lost. This method solves that problem since it obliges us to confirm that we have already processed the desired task.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;is_empty&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This method allows you to check if the queue is empty and it’s useful for handling edge cases like when there are no items to pull.&lt;/p&gt;

&lt;h3&gt;
  
  
  Implementation
&lt;/h3&gt;

&lt;p&gt;Knowing this information, we can start writing our implementation of this data structure. The easiest way to implement this will be through another data structure, an array. To implement this, I will use &lt;a href="https://elixir-lang.org/" rel="noopener noreferrer"&gt;Elixir&lt;/a&gt;, a dynamic, functional programming language that has absorbed the best programming patterns, and I like it a lot.&lt;/p&gt;

&lt;p&gt;Elixir has a great module called &lt;a href="https://hexdocs.pm/elixir/GenServer.html" rel="noopener noreferrer"&gt;GenServer&lt;/a&gt;, it allows you to use isolated processes with unified behavior and also provides the ability to store state and execute code asynchronously. Let’s look at all this with a code snippet:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1q8c4jhlmey0tnj4hk2r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1q8c4jhlmey0tnj4hk2r.png" alt="**GenServer** client part" width="800" height="1116"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this screenshot, you can see the client-side implementation of &lt;em&gt;GenServer&lt;/em&gt; module, which is responsible for the public interface of our queue which we mentioned before. Let’s go through the code line-by-line:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Line 2&lt;/strong&gt; — We use the &lt;em&gt;GenServer&lt;/em&gt; behavior which allows us to create an independent process in the Elixir and store the state. &lt;em&gt;init_arg&lt;/em&gt; in this case will be passed from the &lt;em&gt;GenServer.start_link/3&lt;/em&gt; function, and it will be an array from the client side or an empty array by default.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Line 8&lt;/strong&gt;— This function is responsible for starting a separate process, which will hold our queue with a process identifier.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Line 14&lt;/strong&gt;— Here you can see the presence of all the necessary methods that a public queue interface should have (&lt;em&gt;queue, dequeue, peek, is_empty&lt;/em&gt;). These functions will call methods on the server and return the updated result from them.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwsnnoocog1bszvb3fycq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwsnnoocog1bszvb3fycq.png" alt="**GenServer** server part" width="800" height="1483"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On the server side, we have all the dirty work, namely how the queue works:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Line 43&lt;/strong&gt; — Here you can see how the client call of the &lt;em&gt;enqueue&lt;/em&gt; method calls this callback, which in turn operates by adding the necessary element (the usual push to the end of the array).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Line 48&lt;/strong&gt; — The &lt;em&gt;dequeue&lt;/em&gt; callback checks if the queue is empty, because if it is, the requested item cannot be pulled. If the queue is empty, we return the tuple with an error and a message about the empty queue &lt;em&gt;{:error, :empty}.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Line 58 and 67&lt;/strong&gt; — The same as with &lt;em&gt;dequeue&lt;/em&gt;, we pull an element by index and make an additional check for array emptiness, so that there would be no runtime error when pulling elements.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Line 72&lt;/strong&gt; — Server callback which is responsible for checking the queue for emptiness, the callback calls a private method &lt;em&gt;is_empty&lt;/em&gt; that is not available in the public interface.&lt;/p&gt;

&lt;h3&gt;
  
  
  Result
&lt;/h3&gt;

&lt;p&gt;We’ve implemented our queue using Elixir, which follows the FIFO principle. Let’s take a look at how it works:&lt;/p&gt;


  


&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;Today we learned what a queue is as a data structure in programming, where it is most often used, and how to implement its basic array-based version using Elixir &lt;em&gt;GenServer&lt;/em&gt;. In the next article, we will create a library that can enqueue tasks into queues, persist them in the database with Redis, and process those tasks with workers. Thanks for reading and see you soon!&lt;/p&gt;

</description>
      <category>queue</category>
      <category>elixir</category>
      <category>genserver</category>
      <category>datastructures</category>
    </item>
    <item>
      <title>Learn javascript promises. Part 1 — What is a promise?</title>
      <dc:creator>Volodymyr Potiichuk</dc:creator>
      <pubDate>Thu, 02 Jan 2025 18:11:09 +0000</pubDate>
      <link>https://dev.to/volodymyr_potiichuk/learn-javascript-promises-part-1-what-is-a-promise-4jjj</link>
      <guid>https://dev.to/volodymyr_potiichuk/learn-javascript-promises-part-1-what-is-a-promise-4jjj</guid>
      <description>&lt;h2&gt;
  
  
  Learn javascript promises. Part 1 — What is a promise?
&lt;/h2&gt;

&lt;p&gt;Hey, javascript fans, today I will tell you about promises and how you can learn or understand them better. Many people think that this is a complicated topic and get confused about it, although there is nothing tricky about it, and if you practice it often, you’ll become an expert, I guarantee it. My task today is to help you understand the basics, and to demonstrate the essence of promises and why we need them.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is a promise and why do you need it in javascript?
&lt;/h3&gt;

&lt;p&gt;Imagine you have pledged to your friends that you will continuously exercise each morning and notify them if you do that. Your promise is not blocking you or your friends from doing their life and they are just waiting for your notification about the pledge’s success or failure to make their conclusions or actions about you.&lt;/p&gt;

&lt;p&gt;This small example illustrates the nature of the promise as an event and the two actors that are connected by this event: in this case, it’s the consumer (your friends), and the producer (you):&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjv3nvrrgstvj0mb9hdd8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjv3nvrrgstvj0mb9hdd8.png" alt="Actor’s diagram" width="450" height="162"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And if we simplify it a bit, we can say that in javascript we see the same behavior with the Promise class, which gives us the ability to perform asynchronous tasks without blocking the main thread of the javascript and notify subscribers when we finish our promise.&lt;/p&gt;

&lt;p&gt;Let’s take an example to figure out how to work with it:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnnu26cn8jogrdeid6nou.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnnu26cn8jogrdeid6nou.png" alt="Promise usage example" width="800" height="823"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let’s review this code in order:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;We create a promise instance with an &lt;strong&gt;executor function&lt;/strong&gt; (it’s a function that we pass to the Promise class constructor).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The executor function is the basis of the promise, and this function is called &lt;strong&gt;instantly&lt;/strong&gt; when the promise is created.&lt;/p&gt;

&lt;p&gt;The executor function takes two positional parameters, the first is the resolve method and the second is the reject method. Both of these methods are responsible for finishing promise in some time.&lt;/p&gt;

&lt;p&gt;For example, we promised to get up every morning and exercise for a week. In this case, our promise lasts for a whole week, and if you can’t keep it on day 3, you can call the reject method and everyone who is subscribed to your promise will know about it. Or vice versa, if you keep your promise, then on day 7 you will call the resolve method, which will inform your friends that you have successfully kept your pledge. So you can see that the consumers (your friends) are completely independent of when you tell them the results, they just need to be sure that when they are, they will be able to get them.&lt;/p&gt;

&lt;p&gt;The picture below shows the possible promise states, they are “pending”, “fulfilled” and “rejected”. When a promise is just initiated, its status is “pending”. After it is completed, it is either “fulfilled” or “rejected”, depending on which method was called to complete it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjteh2v829mql64lru8wc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjteh2v829mql64lru8wc.png" alt="Promise states diagram" width="442" height="221"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It is also worth noting that a promise executor cannot be completed more than once, all subsequent calls to resolve or reject methods will be ignored.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;At the moment when you say you finished your promise (by calling the resolve or reject) method, all those who have subscribed to the promise will receive a notification with payload, which can be retrieved inside two special methods of the promise instance. Those methods are: &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/then" rel="noopener noreferrer"&gt;Promise.prototype.then&lt;/a&gt; and &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/catch" rel="noopener noreferrer"&gt;Promise.prototype.catch&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9csinhuozbcsvrjnrwbc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9csinhuozbcsvrjnrwbc.png" alt="Using .then as a handler for both cases (positive and negative)" width="800" height="741"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Using the Promise.prototype.then method, you can handle both positive and negative scenarios at once, as the function accepts two callbacks as in the screenshot above.&lt;/p&gt;

&lt;p&gt;With Promise.prototype.catch you can handle only negative scenarios like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9y7wnq9abww53ohwz1mr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9y7wnq9abww53ohwz1mr.png" alt="Using .then for positive and .catch for negative" width="800" height="741"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;and leave the Promise.prototype.then for positive scenarios.&lt;/p&gt;

&lt;p&gt;Also, promises provide a method that you can use to execute code that should be executed regardless of the result. This method is &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/finally" rel="noopener noreferrer"&gt;Promise.prototype.finally&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgaull8mh2um59fgru08b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgaull8mh2um59fgru08b.png" alt="Using [Promise.prototype.finally](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/finally)" width="800" height="705"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This method &lt;strong&gt;takes nothing&lt;/strong&gt; and &lt;strong&gt;returns nothing&lt;/strong&gt;, as it is designed to complete or clean up previous operations. For example, to show the message to the user or to hide the loader.&lt;/p&gt;

&lt;p&gt;Great, at this point, we’ve covered the basics of promises, so I suggest we move on to a real-world example, like requesting data from the API, transforming it from raw HTTP response to JSON, and outputting it to the console:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F65frtbqnbiio2h1orpfq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F65frtbqnbiio2h1orpfq.png" alt="Demo of simple promise use" width="800" height="558"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see, the result of using it in the real world is not much different from the skills we practiced at the beginning of the article. But still, you may have noticed a couple of new things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;The result from each previous Promise.prototype.then and Promise.prototype.catch handlers are passed to the next Promise handler. It’s good to mention that the Response.json method returns a promise, which in turn, when executed, will return a JSON, and will call the following handler passing that JSON to it.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;In the case of Promise.prototype.finally, it is an “invisible” handler, and you can say that it passes the previous response through itself into the next one.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Awesome, I’m sure at this point you have a better understanding of what a promise is, how to use it, and what states it can have. Also, I want to list below the key points that we went over today (cheatsheet):&lt;/p&gt;

&lt;h3&gt;
  
  
  Summary:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Javascript promise — it is a special object that allows you to execute asynchronous operations and notify consumers of it when it’s done&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Promise instance can have 3 states —”pending”, “fulfilled” and “rejected”&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;There are three main methods of the promise instance — “then”, “catch” and “finally”.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Promise.prototype.then — designed to handle both rejections and successful results&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Promise.prototype.catch — designed to handle rejected results&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Promise.prototype.finally — designed to make some cleanup work&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You can chain your handlers in any order that you want, they will be executed sequentially&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The result from the previous promise goes into the next, so to make the promise chain extensible, you must remember to return a promise from handlers every time except for the “finally” handler.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In future parts of this topic, we’ll cover more advanced promise techniques and delve deeper into its capabilities, so subscribe, like, and stay tuned!&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>es6</category>
      <category>programming</category>
      <category>basic</category>
    </item>
    <item>
      <title>How to automate the launch of your terminal processes (fzf + tmux + teamocil)</title>
      <dc:creator>Volodymyr Potiichuk</dc:creator>
      <pubDate>Wed, 27 Nov 2024 20:00:17 +0000</pubDate>
      <link>https://dev.to/volodymyr_potiichuk/how-to-automate-the-launch-of-your-terminal-processes-fzf-tmux-teamocil-fh4</link>
      <guid>https://dev.to/volodymyr_potiichuk/how-to-automate-the-launch-of-your-terminal-processes-fzf-tmux-teamocil-fh4</guid>
      <description>&lt;p&gt;Do you know this feeling, when you get so tired after the workday, that you want nothing more than to go to sleep as soon as possible? Or you know how it feels when you do boring things from day to day and they affect you negatively because they don’t do you any good?&lt;/p&gt;

&lt;p&gt;If yes, don’t feel lonely, I had the same problem with terminal work until recently, and I ran into it at my job.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Long story short&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;I’ve been working as a front-end developer for a consulting company for the past couple of years, and just like on any other regular job, I’ve spent 90% of my time working on 1–2 projects.&lt;/p&gt;

&lt;p&gt;But, as time went on I got assigned several projects to do (don’t think that my company is the devil and try to get the maximum from my developer soul), but there was a situation where I didn’t have enough workload on any of the projects I worked on.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Problem description&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;The main point is that each project it’s a set of different processes (e.g. backend, frontend, database, etc) that should be started to get your project up and running. And if you have one project, running a couple of processes is not a problem, because it is done one or at most a couple of times a day.&lt;/p&gt;

&lt;p&gt;Now, let’s imagine the situation when I have 3 projects. The required processes are listed in the image below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx11ywg4km5ej4twqkgxb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx11ywg4km5ej4twqkgxb.png" alt="List of processes" width="800" height="336"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see, there are a bunch of processes in each project, and the ports they occupy on my machine overlap a lot (which means I couldn’t run them in parallel even if I wanted to). So every time to switch between projects and run them, I had to close the old one and retype a set of long process commands like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cd backend &amp;amp;&amp;amp; pipenv --python 3.14.0 &amp;amp;&amp;amp; pipenv run gunicorn main:app --workers 4 --worker-class uvicorn.workers.UvicornWorker --bind 0.0.0.0:4000
# ...
cd frontend &amp;amp;&amp;amp; npm run dev
# ...
docker-compose up db redis
# ... and so on
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;to just get my next project up and running. Of course, I was exhausted doing this by typing and searching for the commands every day for each of those projects several times per day…&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe9cj03citgywfk2njzo9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe9cj03citgywfk2njzo9.png" alt="Small picture: how I feel in such moments" width="800" height="478"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you understand, this tedious, useless, and often repeated process will not affect us favorably but will bring unpleasant moments in our work. The time I spent on this switching between projects could be spent sleeping, eating, or resting my eyes, which I think is much cooler. And worth saying that I am a person who believes that everything we do should bring more pleasure, and the work I spend most of my time on is certainly no exception.&lt;/p&gt;

&lt;p&gt;At some point in time, I had an idea of how to fix this, as I realized that the list of commands is almost always the same, it runs from the same folders, and it can quietly be automated.&lt;/p&gt;

&lt;h3&gt;
  
  
  Solution
&lt;/h3&gt;

&lt;p&gt;To solve this problem we need several tools that are good on their own, but if we combine them, we can optimize our workflows by more than 99%. Let’s talk about each of them in more detail and write some code!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flkrlzcuddu0508ay138d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flkrlzcuddu0508ay138d.png" alt="Tools that we are going to use" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1 — Start using tmux application to make your terminal life easier&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;What is &lt;a href="https://github.com/tmux/tmux" rel="noopener noreferrer"&gt;tmux&lt;/a&gt;?&lt;/p&gt;

&lt;p&gt;In simple words, tmux is an application that can manipulate any number of terminals (processes) in a single window and provides an API for working with them. Take a look at the picture below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxrw8skt6aedaicagla6r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxrw8skt6aedaicagla6r.png" alt="The view when you are in tmux" width="800" height="495"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here you can see that I have a “test” session, with 3 windows opened and in any of them I can open any amount of processes. It makes switching between projects and processes a lot faster than replicating terminal instances just to achieve the same behavior.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2 — Tool for managing tmux sessions easily&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The next step will be a tool that can help us achieve fast bootstrapping of the projects (sessions), it can be either &lt;a href="https://github.com/remi/teamocil" rel="noopener noreferrer"&gt;teamocil&lt;/a&gt; or &lt;a href="https://github.com/tmuxinator/tmuxinator" rel="noopener noreferrer"&gt;tmuxinator&lt;/a&gt;, these are the most popular ones and in this tutorial, I will use teamocil. Those tools allow you to write a configuration for a specific session and run this session with many processes and windows in one click.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fot6ax02bo985jxv8rkrg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fot6ax02bo985jxv8rkrg.png" alt="Example of teamocil session configuration" width="800" height="479"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the screenshot above you can see the structure of the simple configuration file, it has a *.yaml format, and some hierarchy that simply replicates the architecture of tmux.&lt;/p&gt;

&lt;p&gt;Such config leads to the ability to use one command for bootstrapping the tmux session:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;teamocil &amp;lt;config_name&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and voilà, we will get up and running session with one command:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fei07xduhrpvxr8a2ci0n.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fei07xduhrpvxr8a2ci0n.gif" alt="Example of bootstrapping whole session in just one click" width="1000" height="765"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see, we have already eliminated one of the problems, we don’t need to type and remember all commands, open new terminal windows, and switch folders every time, those magic tools will do it for us.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3 — Fuzzy finder&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/junegunn/fzf" rel="noopener noreferrer"&gt;FZF&lt;/a&gt; (Fuzzy finder) is a command-line application that allows you to turn any list into an interactive menu with support for awesome fuzzy search, which you can read more about &lt;a href="https://github.com/junegunn/fzf?tab=readme-ov-file#search-syntax" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjkyywvel89c859e9jauv.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjkyywvel89c859e9jauv.gif" alt="Example of FZF work" width="1000" height="627"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I think that you should get familiar with this application as soon as possible because it can come in handy everywhere where you need to search for something in a list, think about GitHub branches for example.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 4 — Automate with a shell scripting&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;At this point, we already know how to open sessions in one click and do a smart (fuzzy) search, now we need to put it all together with shell (sh) scripting.&lt;/p&gt;

&lt;p&gt;I am sure you are already familiar with this and used some commands such as “change directory” or “copy” and so on. Thanks to shell scripting it is possible to automate many processes, for example, sending messages to Slack, or pulling the database from the environment, and in our case, we can make a script that will allow us to control our sessions via simple GUI and shortcuts without typing any commands.&lt;/p&gt;

&lt;p&gt;In my case, I use the &lt;a href="https://en.wikipedia.org/wiki/Bash_(Unix_shell)" rel="noopener noreferrer"&gt;bash shell (Bourne-Again SHell)&lt;/a&gt; as it is a slightly improved version of sh. But you can also use the “sh” by itself or tools that allow you to translate your code from any desired language into the shell, for example, &lt;a href="https://github.com/google/zx" rel="noopener noreferrer"&gt;“zx” from Google&lt;/a&gt;, which allows you to write your scripts in node.js and run them as shell scripts.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Coding part&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;To meet our expectations with the optimization we need to create an application that can do three things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Start selected session&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Delete selected session&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Navigate between running sessions&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let’s start with the first point which we can achieve with teamocil and fuzzy finder, FZF will be as a GUI and teamocil as a tool that we will call when a certain session is selected (to bootstrap our session). A simple version of the script will look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# start selected session
teamocil $(ls $HOME/.teamocil/ | awk -F. '{print $1}' | fzf)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Don’t be afraid if you don’t understand it right now, let’s see what we did there:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Here we take the folder with our session configs and get a list of files from there
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Imagine we have such file structure in our $HOME/.teamocil folder:
# - first_project.yml
# - second_project.yml
# - third_project.yml
# - ...*.yml

ls $HOME/.teamocil/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;We process these files via the “awk” tool to get only the session name (without a file extension) and pass them to FZF.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;awk -F. '{print $1}' | fzf

# In runtime it will look like this
# first_project.yml     first_project
# second_project.yml -&amp;gt; second_project | fzf
# third_project.yml.    third_project
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;The result we get from the FZF is forwarded to teamocil, which bootstraps the actual session.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Already with this small command, we can start our sessions in an instant like that:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0hhoevpa0ogegckrjsuo.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0hhoevpa0ogegckrjsuo.gif" alt="Starting sessions" width="1000" height="570"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Alright, we finished with the creation of the sessions, and now we can proceed with switching between sessions and deleting them:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# change active session
tmux switch -t $(tmux list-sessions | awk -F: '{print $1}' | fzf)

# delete session
tmux kill-session -t $(tmux list-sessions | awk -F: '{print $1}' | fzf)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You see, it’s almost the same as the previous one, however, we are checking the list of the active sessions with the tmux command and passing it to the “switch” or “kill-session” native command of the tmux depending on the desired behavior. That’s it, demonstration of our script is there:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk877et6nlw0tvjtyi18i.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk877et6nlw0tvjtyi18i.gif" alt="Demo" width="800" height="426"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At this point, we have one last thing to do: unify our scripts into one whole like a multi-tool, where we can create, delete, and switch between sessions. A cool way to do this is to use the &lt;a href="https://man7.org/linux/man-pages/man1/tmux.1.html" rel="noopener noreferrer"&gt;tmux “display-popup” command&lt;/a&gt;, which allows you to display a window on top of all the processes in tmux, and that’s where I’d like to put the script implementation.&lt;/p&gt;

&lt;p&gt;In my tmux.conf:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bind f display-popup -w 75% -h 50% -E "~/.config/fzf_tmux_sessions/bin/fzf_tmux_sessions.sh"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command will open a tmux popup with a width of 75% and height of 50%, invoke the code from the specified file.&lt;/p&gt;

&lt;p&gt;That’s it, with small improvements to our code above we will get something like this:&lt;/p&gt;


  


&lt;p&gt;It turns out that in this case, you can switch between different projects in less than 5 seconds instead of 5-10 minutes. Isn’t that cool, imagine how much time you save if you switch between projects 5 times a day or just bootstrapping large projects every day? If you want to see more, you can find the full code of this script in my &lt;a href="https://github.com/ExuCounter/dotfiles/tree/master/fzf_tmux_sessions" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; repo.&lt;/p&gt;

&lt;p&gt;In conclusion, I can say that this project has taught me quite a lot of skills, and I think that if you want to optimize something in your life, do not hesitate, it is very exciting and educational.&lt;/p&gt;

&lt;p&gt;Thank you so much for reading this article, I hope it was helpful to you, see you next time!&lt;/p&gt;

</description>
      <category>automation</category>
      <category>terminal</category>
      <category>bash</category>
      <category>linux</category>
    </item>
  </channel>
</rss>
