Part 3 · A Working Day

#24Nothing Runs Between Two Clicks

The long report, the busy indicator, the timer and the job.

3 min read · 24 of 42

#7 made a claim that sounds harmless until the first long report: nothing runs between two clicks. No thread waits, no promise resolves later, no controller ticks in the background. A roundtrip starts when the browser posts, ends when main( ) returns, and in between the app is a row in a table.

The dialog world handled the long case with an hourglass. A report ran for two minutes in the foreground, SAPGUI_PROGRESS_INDICATOR told the user how far it had got, and anything longer went to SM36. All three have an answer here, and the answers are as unexciting as the questions.

A long main( ) is a long dialog step. The roundtrip occupies one dialog work process for as long as main( ) runs, and the runtime limit Basis configured for dialog steps applies to it the way it always did. The browser waits patiently: after one second the UI5 busy indicator covers the screen, clicks that land meanwhile are dropped rather than queued, and only after ten minutes does the frontend give up with No backend response within 600 seconds. For a SELECT that takes four seconds, nothing has to be written at all.

What does not exist is progress during a roundtrip. The response is one string that arrives when main( ) is done, so a percentage written halfway through has nobody to read it.

Work that is long because it is big is split. A thousand documents are a hundred per roundtrip, with the position in an attribute and the next chunk started by the app itself. That turns one blocking request into a sequence the user can watch — and cancel, which the two-minute dialog step never offered.

Work that is long because it is long goes to a job. The app schedules it — JOB_OPEN, SUBMIT ... VIA JOB, JOB_CLOSE, or the cl_apj_rt_api of ABAP Cloud — keeps the job’s name in an attribute, and asks for its status on a timer:

    CASE abap_true.
      WHEN client->check_on_event( `START` ).
        job_schedule( ).            " remembers job name and count
        poll( ).
      WHEN client->check_on_event( `POLL` ).
        status_read( ).             " TBTCO, or the API of ABAP Cloud
        IF status <> `F` AND status <> `A`.
          poll( ).                  " not finished: ask again
        ENDIF.
    ENDCASE.
  METHOD poll.
    " one backend event in three seconds - without the busy overlay
    client->follow_up_action( val   = client->cs_event-start_timer
                              t_arg = VALUE #( ( `POLL` ) ( `3000` ) ( `X` ) ) ).
  ENDMETHOD.

cs_event-start_timer raises one backend event after a delay, and it is deliberately small: one timer per app, and every roundtrip cancels it, so a timer only keeps ticking while each response re-arms it. A poll ends the moment the app stops asking, and an app the user has left polls nothing. The third argument keeps the overlay down for the tick, so the screen stays usable while the poll runs.

The job itself runs where batch work always ran, in a background work process, with its log in SM37 and none of the dialog limits. The screen is what it was in the GUI: a view on the job, not the job.

Push instead of poll. Where polling every few seconds is too slow or too chatty, the frontend’s z2ui5:Websocket control listens on an ABAP Push Channel: the job — or anything else on the server — publishes through AMC, and every message arrives in the app as an ordinary backend event, with no JavaScript written (WebSocket). That is an APC and an AMC to set up and operate, which is why the timer comes first.

The timer, with the sample for a progress indicator and one for a view that refreshes itself, is on Timer.

The hourglass is now a busy indicator, the progress bar is a poll, and the job is still a job.