1С зависает? Переносим код в фон через БСП + показываем прогресс — Transcript
Full transcript
- 0:00Before you is a slightly customized 1Sf
- 0:01configuration. A "Manual Discount"
- 0:04button has been added to the customer
- 0:05order list form, and here is how it
- 0:07works. The user selects many orders in
- 0:09the list, clicks the "Manual Discount"
- 0:11button, enters a discount percentage,
- 0:12and the discount is applied to all
- 0:14selected orders. Essentially, the order
- 0:16totals are recalculated, and the orders
- 0:18are then reposted. Here is how it works
- 0:19right now. I will select a large number
- 0:21of orders at once. Next, I click the
- 0:23button and specify a discount
- 0:24percentage, say 10%. We click "OK". And
- 0:27the programmatic modification of the
- 0:28orders takes place. But while this is
- 0:30happening, I cannot do anything else.
- 0:31That is, the interface is blocked, and
- 0:33I cannot navigate anywhere. Only after
- 0:35the operation finishes will I see that
- 0:3736 orders have been processed. How can
- 0:40I ensure that the program interface
- 0:42does not block when I click the button?
- 0:44I would like to continue working, such
- 0:47as opening other lists or generating
- 0:49reports, without waiting for this
- 0:51operation to finish. Hi, my name is
- 0:53Vanya, and this is the Iron Skills
- 0:55channel. Here we talk about 1C
- 0:56development. And in this video, we will
- 0:59discuss background execution of
- 1:00long-running operations. Let's not
- 1:02waste time, let's go. Let's start with
- 1:04the fact that there is a development
- 1:06standard. I will leave a link to it in
- 1:07the description below this video. The
- 1:10standard states that all long-running
- 1:12server calls that may take more than 8
- 1:14seconds in normal user scenarios should
- 1:16be performed asynchronously using a
- 1:18background job. It then describes in
- 1:20detail which tools exist for this
- 1:22purpose, specifically in the Standard
- 1:24Subsystems Library. And we can use this
- 1:26in all standard configurations, as they
- 1:28contain the SSL. And I will show you
- 1:31how to use it right now with examples.
- 1:33Let's switch to the Configurator of
- 1:34this database. First, I will show how
- 1:36the customization is implemented. It is
- 1:37implemented via a configuration
- 1:39extension; that is, an extension was
- 1:40created, and the list form was added to
- 1:42it. Then, first of all, in the form's "
- 1:44OnCreateAtServer" event handler, the
- 1:46command and the corresponding button
- 1:48are created programmatically. The
- 1:50command handler is a client procedure
- 1:52called "Apply Manual Discount." In this
- 1:55procedure, we retrieve the selected
- 1:56orders from the list and check how many
- 1:58there are. If there are none, then
- 2:00there is no point in proceeding. If at
- 2:01least one order is selected, we prompt
- 2:03the user for the discount percentage.
- 2:05Then, a server function is called, to
- 2:07which we pass an array of order
- 2:08references and the discount percentage
- 2:10to be applied to these orders. Within
- 2:13this function, code is written that
- 2:15loops through all the orders. It
- 2:17retrieves the object, modifies the data
- 2:19in the tabular section—that is, it
- 2:21sets the discount percentage for each
- 2:23row and calls the function to
- 2:24recalculate the row totals—after
- 2:26which it programmatically writes or
- 2:27reposts the document, depending on
- 2:29whether it was just written or posted.
- 2:32And this function counts the number of
- 2:34modified orders. This quantity is
- 2:35returned to the client, after which the
- 2:37list is updated on the client side so
- 2:39the user immediately sees that the
- 2:41totals have changed, and a notification
- 2:42is displayed stating how many orders
- 2:44were modified. Now, let's look at how
- 2:46to move server-side code to background
- 2:48execution. For this, we will use the
- 2:49SSL (Standard Subsystems Library) and
- 2:51procedures located in common modules.
- 2:53They are called: Long-running
- 2:54operations and Long-running operations
- 2:56client. We need to ensure that this
- 2:59server function, which handles
- 3:00programmatic modification of orders and
- 3:02their reposting, runs in the background
- 3:04. That is, a separate background job
- 3:06should be responsible for the execution
- 3:07. For the job to be able to execute
- 3:09this procedure, it must be placed in a
- 3:11different module. This could be a
- 3:13common module, an object manager module
- 3:15, or an object module. Most often, a
- 3:18common module or manager module is used
- 3:21. I will take the path of creating a
- 3:23common module. So, I create a common
- 3:24module and name it, for example, like
- 3:26this. "CustomerOrderProcessing". For
- 3:29this, we also check the "External
- 3:31connection" checkbox. And we move this
- 3:32function from the form module to the
- 3:34common module. Here we remove the
- 3:36compilation directive. We will also
- 3:38slightly change the name of this
- 3:39function. I will leave it as "Apply
- 3:41manual discount to orders". We just
- 3:43make it mandatory to export so that it
- 3:45can be called. And let's designate a
- 3:47region here: "Program interface". Where
- 3:49we will place this function. Checking
- 3:51the syntax. No errors. We return back
- 3:53to the form module. Now we need to
- 3:56ensure that the common module function
- 3:58is called, but not directly; rather, it
- 4:00should be called as part of a
- 4:01background job. To do this, we will
- 4:04need to call specific procedures from
- 4:06the Long-running operations common
- 4:07module. The code that currently calls
- 4:09the function will be commented out. And
- 4:11I will place the client-side part
- 4:12within separate procedures, because I
- 4:14will be showing you different examples.
- 4:16So, the first example. Let's look at
- 4:18how to ensure this server procedure
- 4:20executes in the background without
- 4:21displaying a progress bar. I will be
- 4:23copying code fragments. Some procedures
- 4:25are already prepared in advance. I will
- 4:26also attach them to the description
- 4:28under this video later. On the client,
- 4:30we need to initiate a call to the form
- 4:32module server function. We will add
- 4:34this function here now as well. It will
- 4:35be called "Start discount application".
- 4:37In this server function, we have
- 4:39references to the orders that need to
- 4:41be changed and the discount percentage.
- 4:43Our main task is to call the
- 4:44Long-running operations common module,
- 4:46the "Execute function" method. A
- 4:47special structure, execution parameters
- 4:49, is passed there. There is a
- 4:50constructor for this structure. It is
- 4:52another function of the common module.
- 4:53"Long-running operations" is called "
- 4:54Function execution parameters". The
- 4:55second parameter is the path to the
- 4:57function or procedure that should be
- 4:59executed in the background. In our case
- 5:00, it is the common module "
- 5:02CustomerOrderProcessing" and then the
- 5:03function name. "Apply manual discount".
- 5:06The procedure or function of the common
- 5:07module that we call in the background
- 5:08can have parameters, and we have them:
- 5:10these are orders and the discount
- 5:11percentage. And for these parameters to
- 5:14be passed, we must list them in the "
- 5:15execute function" method after the
- 5:17procedure or function name, separated
- 5:19by commas, in the exact order they
- 5:20should be passed there. Moreover, there
- 5:23can be up to seven parameters. If you
- 5:24go to the common module and look at the
- 5:26syntax for the "execute function," it
- 5:28will state that we can have up to seven
- 5:30parameters, which can simply be passed
- 5:32one after another, separated by commas.
- 5:33And accordingly, the orders will be
- 5:35passed here, and the discount
- 5:37percentage here. The "execute function"
- 5:39method returns a special structure,
- 5:40essentially containing a description of
- 5:42the started background job, and we
- 5:43receive it on the client side to track
- 5:45when this job is finished. The next
- 5:47stage on the client side is waiting for
- 5:50the job to complete. For this, we use
- 5:52the client-side common module "
- 5:53Long-Running Operations Client" and the
- 5:55special method "wait for completion."
- 5:56Here we must pass the structure
- 5:58describing the started job as the first
- 6:00parameter and the completion
- 6:02notification as the second parameter.
- 6:04This determines which procedure will be
- 6:05called when the job finishes. Either
- 6:07successfully, or, for example, if the
- 6:09user cancels it. In our case, this
- 6:11procedure will be called "after
- 6:12applying discount." I will copy this
- 6:14procedure here into our module. And in
- 6:16the procedure that will be called after
- 6:18the background job finishes, we will
- 6:20have two parameters. These are the
- 6:22result and additional parameters. The
- 6:24result is a structure. And this
- 6:26structure contains an important key:
- 6:28the status. By the status, we can
- 6:30understand whether the job was executed
- 6:32successfully or if an error occurred.
- 6:35The possible scenarios can all be
- 6:36viewed at the level of this "wait for
- 6:37completion" method. Let me drill down
- 6:40into this. Let's open the description.
- 6:42And it states here that the result is a
- 6:44new long-running operation result.
- 6:46Meaning, it is a structure like this.
- 6:47This is what we will get as a result.
- 6:49Specifically: status, result address,
- 6:51and error information, if any error
- 6:53occurred. And a job identifier may also
- 6:55be present there. The result address is
- 6:57the address of data in the temporary
- 6:59storage where the function execution
- 7:01result is located. For example, look
- 7:03here. In our case, in the common module
- 7:05, where we moved this function, it
- 7:06returns a value, which is the number of
- 7:08processed orders. The function will be
- 7:10executed by a background job. And to
- 7:12get the result of this function's
- 7:14execution, we must use this very key:
- 7:16the result address. I will return to
- 7:18the form module. In the "after applying
- 7:20discount" procedure, which will be
- 7:22called when the job is done, we can
- 7:24access the result address via the
- 7:26result and retrieve the outcome from
- 7:28the temporary storage. And this will be
- 7:30the number of processed orders in this
- 7:32case. Well, if, for example, you were
- 7:34calling some function that returns, say
- 7:36, a reference to a document, or an
- 7:37array of references, then we would get
- 7:39an array of references. In other words,
- 7:41this is what the function called in the
- 7:42background essentially returns. And the
- 7:43logic after the background job
- 7:45completes is as follows. We check the
- 7:46execution status. If it is an error, we
- 7:49display an error message. If the job
- 7:50was canceled, we simply exit the
- 7:52procedure. And if the background job
- 7:54completed successfully, the third
- 7:55scenario, in this case, we get the
- 7:57result of the function execution,
- 7:58retrieve it from the temporary storage,
- 8:00update the list, and show a
- 8:02notification to the user that the job
- 8:03is done and a certain number of orders
- 8:05were processed. Well, now let's see how
- 8:07this will work. I see there is an error
- 8:09, yes, I am not passing two more
- 8:11parameters. We will fix that now.
- 8:13Checking the syntax. Updating the
- 8:15database configuration. Starting the
- 8:16session. Going to the customer orders
- 8:19list. I will select several documents.
- 8:21I will click the manual discount button
- 8:22. And let's assign 5%. Clicking OK. And
- 8:26a window like this appears. The
- 8:27interface is not blocked, so I can
- 8:29calmly continue working. And we see
- 8:30that a lengthy operation is being
- 8:31performed. When it finished, we
- 8:33received a notification that a certain
- 8:35number of orders were processed. Now
- 8:36let's consider the next option. Suppose
- 8:39that right now, when the lengthy
- 8:41operation window opens, we see some
- 8:43standard text here. Lengthy operation.
- 8:45Please wait. If we want to show our own
- 8:47text, how is this implemented? I will
- 8:50describe this within a separate client
- 8:51procedure. It will be called "Apply
- 8:53discount in background option two," and
- 8:55through its example, I will show you
- 8:57how to manage the parameters of this
- 8:59window. I will also add the procedure
- 9:01to the module. The difference will be
- 9:03that here we must describe a special
- 9:05structure. It is called waiting
- 9:06parameters and can be obtained via the
- 9:08common module "Lengthy operations
- 9:10client" and the "waiting parameters"
- 9:11function. The current form is passed
- 9:13there. And then, in this structure,
- 9:15there is, for example, the window title
- 9:17and the message text that we want to
- 9:19show. And we pass the waiting
- 9:20parameters to the "wait for completion"
- 9:21method. So, in the first option, we
- 9:23only passed the structure with
- 9:24information about the lengthy operation
- 9:26and the completion notification to "
- 9:27wait for completion." Here we can also
- 9:29pass these waiting parameters, which
- 9:31allow overriding the window. I will
- 9:33comment out the call to the first
- 9:34client procedure and see what the
- 9:35difference will be. Otherwise,
- 9:37everything remains the same. So here we
- 9:38didn't touch anything else, only the
- 9:40call to the "wait for completion"
- 9:42method on the client. Going to Sales,
- 9:45customer orders. I will select more
- 9:47orders. Manual discount. And let's
- 9:48reset it, for example. I press zero
- 9:50here. OK. And now a window appears with
- 9:53our text. Applying the discount and
- 9:55performing changes to selected customer
- 9:57orders. Moving on. Now I also want to
- 10:00see execution progress to understand
- 10:02what percentage of the task is complete
- 10:04and how this scenario is implemented.
- 10:06To implement the display of execution
- 10:08progress, we first need to change the
- 10:09waiting parameters. When calling the
- 10:11wait for completion method. I will copy
- 10:13the procedure name. We will now call a
- 10:16procedure that will be called option
- 10:18three. I will copy it and show the
- 10:20difference specifically on the client.
- 10:22So, we have initiated a long-running
- 10:24operation. Then we want to wait for its
- 10:26completion, and we set the wait
- 10:28parameters. In the wait parameters, we
- 10:30have previously set the title and
- 10:32message text. And to also show
- 10:34execution progress, we enable a
- 10:35parameter here called display execution
- 10:37progress, set to true. There is also an
- 10:39interval parameter that allows you to
- 10:41set the polling interval for task
- 10:42readiness. You can look at more details
- 10:44in the description of this wait
- 10:45parameter function. Here, by going into
- 10:47the comments, you will see that the
- 10:49interval is the number of seconds
- 10:50between readiness checks. By default it
- 10:53is zero, which means that after each
- 10:54check the interval increases from one
- 10:56to 15 seconds with a factor of 1, but
- 10:58we will just set it to one. That is,
- 11:00the task will be polled for its
- 11:02readiness every second. That is not all
- 11:05. Besides changing the wait parameters,
- 11:08we must also ensure our server function
- 11:10reports the completion percentage,
- 11:11because how would the platform know how
- 11:13ready our function is, since the logic
- 11:15there can be completely different?
- 11:18Therefore, we go to our main server
- 11:20function, which runs in the background,
- 11:22and add a call to a special method from
- 11:24the long-running operation common
- 11:26module. When our order is saved, we
- 11:28increment the counter of processed
- 11:31orders. Right here we call long-running
- 11:33operations report progress. And we pass
- 11:36the progress value in percent that we
- 11:38calculated, plus some text so it is
- 11:40clear how many orders we have processed
- 11:43. So, to implement the display of
- 11:45execution progress, first, we must
- 11:47enable progress display in the wait
- 11:49parameters when calling the wait for
- 11:51completion method. That is one. And
- 11:53second, in our main server procedure
- 11:55running in the background, we must
- 11:57explicitly add a progress message.
- 11:58Updating the database configuration.
- 12:00Let's launch a session and see what has
- 12:02changed. So, Sales, Customer Orders.
- 12:04Select a larger list of documents,
- 12:06manual discount. Set 5%, click OK. So,
- 12:09a window opens. And here we can already
- 12:12see the task execution in percent.
- 12:15Result: 27 orders processed. I will
- 12:18also show you a diagram of how this
- 12:19works. We have a client, a server, and
- 12:21a background task. On the client side,
- 12:23we receive an array of references to
- 12:25the orders that need to be changed from
- 12:26the user, along with the discount
- 12:27percentage. Next, on the server, again
- 12:29in the form module, we initiate the
- 12:31launch of a background job. For this,
- 12:33we use the "Long-running operations"
- 12:35common module and the "Execute function
- 12:37" or "Execute procedure" methods. We
- 12:39pass the name of the procedure or
- 12:40function to be executed there, plus a
- 12:42set of parameters. From that point on,
- 12:43the SSL (Standard Subsystems Library)
- 12:44handles everything for us. That is, the
- 12:46SSL method launches the background job
- 12:48and receives information about it in
- 12:50the form of a structure. We then
- 12:53receive this structure on the client
- 12:55side and can use it to wait for the job
- 12:58to complete. Having received the
- 13:01structure with information about the
- 13:03background job from the server function
- 13:05, we call the "Long-running operations
- 13:08client" and the "Wait for completion"
- 13:10method, passing in this long-running
- 13:12operation structure along with the info
- 13:14on which of our procedures to call when
- 13:16the job finishes. Then, the job
- 13:19continues to run and run. If we need to
- 13:21report execution progress, we forcibly
- 13:23add a call to the "Report progress"
- 13:24method within the function running in
- 13:26the background. That means we calculate
- 13:28the progress ourselves, and it is also
- 13:30displayed on the form. When the job
- 13:32finishes, the platform calls our
- 13:34procedure that we specified as the
- 13:36completion handler, passing the result,
- 13:38which contains the job execution status
- 13:41plus the address of the background
- 13:43function's result. Based on the status,
- 13:45we determine what the final outcome was
- 13:47. That is, whether the job completed
- 13:49successfully, failed with an error, or
- 13:50was canceled. If it completed, we can
- 13:53then use the result address to retrieve
- 13:54the function's execution result from
- 13:56temporary storage and do something with
- 13:58it. For example, in our case, we simply
- 14:00display information about how many
- 14:01orders were processed. That is how it
- 14:03works in a single thread, but there is
- 14:05also an option for multi-threaded
- 14:07execution of certain code in the
- 14:09background. I will show you how to do
- 14:11this now. First, I will explain the
- 14:13scheme of how it works. Let's start
- 14:14with the common module function.
- 14:16Currently, the function running in the
- 14:18background processes all orders. That
- 14:20is, we have an "orders" parameter,
- 14:21which is an array of references to the
- 14:23orders that need processing. If we want
- 14:25to process orders in multiple threads,
- 14:27we need to split this list into chunks.
- 14:29In our case, a chunk can be a single
- 14:31order. This means we need a function
- 14:34that will apply a discount to one order
- 14:36rather than to a list of orders. And,
- 14:38in principle, we can describe this
- 14:40function right here and now. I already
- 14:42have it prepared. Everything in it is
- 14:44the same, the logic is absolutely
- 14:45identical, just a different parameter.
- 14:46That is the order reference. And there
- 14:48is no loop here. We simply get the
- 14:50order, modify it, and repost it. Now
- 14:52let's move on. When we have a function
- 14:55like this that processes exactly one
- 14:56order, we can process them in multiple
- 14:58threads. The logic is as follows. On
- 15:01the client, we form a map with a set of
- 15:03possible function parameters. In other
- 15:06words, these are the sets of parameters
- 15:08with which this function needs to be
- 15:10called. So, first we must call this
- 15:12function to apply a discount to one
- 15:14order by passing the first order and a
- 15:15discount percentage, say 5%, here. Then
- 15:17the second order and a discount
- 15:18percentage of five. And so we generate
- 15:20this entire set on the client. Then, on
- 15:22the server, we call the common
- 15:24long-running operations module. It has
- 15:27a specific method called execute
- 15:29function in multiple threads. And we
- 15:31pass that set of parameters and the
- 15:33function we want to call with different
- 15:34sets of parameters to it. From there,
- 15:36the BSP will handle everything for us.
- 15:37The method from the common long-running
- 15:39operations module. It first starts a
- 15:41control thread. This is a parent task
- 15:43that subsequently spawns execution
- 15:45threads—these are background jobs
- 15:47that will call the function with
- 15:49different parameter sets. So, the first
- 15:52thread will take a set of parameters
- 15:54from the queue, say, order 1 with 5%,
- 15:55and start working with it. The second
- 15:57thread will take the next order from
- 15:59the queue and start working with it.
- 16:00And so they will be able to process
- 16:02this entire queue in parallel. When all
- 16:04tasks are completed, the control thread
- 16:07receives this information and finishes
- 16:09its task. Then a procedure is called in
- 16:12the form module where we receive the
- 16:14execution result. Well, the logic here
- 16:16is the same as in the first option.
- 16:17That is, we get the result, which will
- 16:19include the execution status—
- 16:20completed, error, or canceled—and we
- 16:22can get the address of the function
- 16:24execution result. An important point is
- 16:26that multithreaded background execution
- 16:28of code is only possible if we can
- 16:30truly process these portions of data
- 16:32independently of each other. For
- 16:36example, if we had a slightly different
- 16:37task, like needing to repost documents
- 16:39for the same warehouse. We wouldn't be
- 16:42able to do this in parallel anyway,
- 16:44because you cannot simultaneously post
- 16:46two documents deducting the same items
- 16:48from one warehouse, as it would cause
- 16:50lock contention. Therefore, it is
- 16:52important to keep in mind that this
- 16:54multithreading can only be applied in
- 16:56cases where we can truly process or
- 16:58modify data independently of each other
- 17:00. In our case, this is possible. I mean
- 17:02, posting one order will not lead to me
- 17:04interfering with the operation of
- 17:05another thread in parallel. This is the
- 17:07general scheme. Now let's return to the
- 17:09configurator and implement this. I have
- 17:10already added the function here. So, in
- 17:12the common module, we have the function
- 17:13that processes exactly one order and
- 17:14applies a discount to it. Now we move
- 17:16on to the form module. And here we will
- 17:19need to call "Apply Discount in
- 17:21Background Option 4". Let's add this
- 17:24procedure here. The difference will be
- 17:26as follows. Regarding the waiting
- 17:28parameters, it is all the same. That is
- 17:29, we will also display the execution
- 17:31progress here. And we also call "wait
- 17:32for completion", but the difference
- 17:34will be in the server function itself,
- 17:36because it won't be called like in the
- 17:38other options where we use the same "
- 17:39Start Applying Discount", here we need
- 17:41to call it slightly differently,
- 17:42because it needs to form a set of
- 17:44parameters. Therefore, I am defining a
- 17:46separate function that will be called "
- 17:48Start Applying Discount in Multiple
- 17:49Threads". Here, first, we create a map
- 17:51of parameter sets on the client, and
- 17:54using the array of orders and the
- 17:56discount percentage, we form this map
- 17:58and populate it. The key in this map,
- 18:00in our case, will be the reference to
- 18:01the order, and the set of parameters is
- 18:03an array with our function's parameters
- 18:05. We have two of them, so we add them
- 18:07here. Then we call the "Execute
- 18:08Function in Multiple Threads" function
- 18:09from the common module of long-running
- 18:11operations. We pass the name of the
- 18:13function to be executed in the
- 18:14background, the execution parameters,
- 18:16and this map with the sets of
- 18:17parameters. And that is basically it.
- 18:21Note that we do not specify the number
- 18:22of threads to be launched anywhere here
- 18:24. The fact is that the number of
- 18:26threads here is determined
- 18:27automatically by the SLP (Standard
- 18:28Subsystems Library) itself. There is a
- 18:31constant that allows you to limit the
- 18:33maximum number of threads. It is called
- 18:35"Number of long-running operation
- 18:37threads". So, I will update the
- 18:38database configuration. Starting the
- 18:40session. My database is file-based, so
- 18:42we won't notice any effect right now,
- 18:43of course. But first, let me go to "
- 18:45Functions for Technical Specialist" and
- 18:47set the value of this constant. "Number
- 18:49of long-running operation threads".
- 18:51Let's set it to four. Then let's go to
- 18:54Sales, Customer Orders. Let's select
- 18:56more orders. I click "Manual discount"
- 18:58and, let's say, 10%, applying it. Click
- 19:01"OK". And the background task is
- 19:03running. But at the same time, the
- 19:04execution is now taking place in
- 19:06multiple threads. Well, it's clear that
- 19:07this will only work fully in the
- 19:09client-server version. In the file
- 19:11version, I will essentially still have
- 19:12only one thread. Thank you for watching
- 19:14. You will find all the examples from
- 19:16today's video in the description.
- 19:17Subscribe to us, give us a like. My
- 19:19name is Vanya, this is the Iron Skills
- 19:21channel. Here we talk about 1C
- 19:22development. Bye. M.
About this transcript
This page contains the full transcript of 1С зависает? Переносим код в фон через БСП + показываем прогресс by IRONSKILLS - Курсы по 1С, generated from the public captions YouTube serves with the video. The transcript has 3,942 words across 617 segments, with the original timestamps preserved so you can click any line to jump to that moment in the embedded player.
What you can do with it
Use the transcript to take notes, quote the speaker, build a study guide, generate a summary with ChatGPT or Claude via the YouTube Summary tool, or export it as a timed subtitle file with YouTube to SRT. You can also re-open it in the transcriber to translate the transcript into 100+ languages.
Free YouTube transcript tool
YouTube2Text is a free YouTube transcript generator — no signup, no daily limit. Paste any YouTube link and get the full transcript instantly, with timestamps, click-to-jump, translation to 100+ languages, AI prompts for ChatGPT, Claude, and Gemini, and exports to TXT, SRT, VTT, or Markdown.