如何将API调用的进度传递给服务员

问题描述 投票:0回答:1

我有一个闪亮的应用程序模块,其中使用

callr
在模块中进行 API 调用,结果显示在表格中。 API调用需要一些时间,我想显示一个实时或模拟的过程(尝试过闪亮的进度,这种方法有效,但信息不丰富)。想传达“有什么落后了,就有进步”。我不知道如何传递API调用的实时进度。然后,根据waiter包上的例子,添加了sleep。然而,这并没有达到目的。如果有更好的方法来显示真实进度,而不需要像示例中那样的 for 循环,那将是理想的。

  1. 如何将API调用的实时进度传递给服务员加载?
  2. 如果我在模块 UI 中有加载程序,它就不起作用,必须将其放置在主应用程序 UI 中,如何为每个模块设置特定的服务员,这样我就可以选择根据任务选择服务员,例如通知、页面或标题。

我无法制作 API 调用的简化版本,所以我只是留下了评论。我不知道有任何开源,我可以使用 httr 等创建 API 调用的示例。

下面是我的工作代码,显示一个页面,但不显示任何进度

library(shiny)
library(waiter)
# Define the UI function for the API call module
AsyncApiCallModuleUI <- function(id) {
  ns <- NS(id)

  # waiterShowOnLoad(
  #   color = "#f7fff7",
  #   hostess_loader(
  #     "loader",
  #     preset = "circle",
  #     text_color = "black",
  #     class = "label-center",
  #     center_page = TRUE
  #   )
  # )
  shiny::actionButton(
    inputId = ns("apiButton"), label = "Refresh API Call",
    icon = icon("rocket")
  )
}

AsyncApiCallModule <- function(id, args) {
  shiny::moduleServer(id, function(input, output, session) {
    # Create the progress bar
    # progress <- shiny::Progress$new(session)
    hostess <- Hostess$new("loader")
    shiny::observe({
      # Start the progress bar
      # progress$set(message = "API call in progress", value = 0.1)
      hostess$set(value = 0.1)
      # r_bg API call goes gere
      Sys.sleep(runif(1) / 2)
      # Update the progress bar
      # progress$inc(0.1)  # Increment the progress bar by 10%
      hostess$inc(0.1)

      # Complete the progress bar
      # progress$set(value = 1)
      hostess$set(1)
      # progress$close()
      waiter_hide()
    })

    base::return(mtcars)
  })
}

ui <- shiny::fluidPage(
  useWaiter(),
  useHostess(),
  waiterShowOnLoad(
    color = "#f7fff7",
    hostess_loader(
      "loader",
      preset = "circle",
      text_color = "black",
      class = "label-center",
      center_page = TRUE
    )
  ),
  shinyjs::useShinyjs(),
  AsyncApiCallModuleUI("mtcars_srv"),
  DT::dataTableOutput("mtcars_table")
)

server <- function(input, output, session) {
  mtcars_result <- AsyncApiCallModule(
    id = "mtcars_srv", args
  )

  output$mtcars_table <- DT::renderDataTable({
    DT::datatable(mtcars_result)
  })
}

shiny::shinyApp(ui, server)
静态 R Markdown 文档不支持闪亮的应用程序

创建于 2024-03-21,使用 reprex v2.1.0

shiny progress-bar httr waiter
1个回答
0
投票

以下是如何在 JavaScript 中使用进度条执行下载。

文件 xhr.js,放入应用程序的 www 子文件夹中:

Shiny.addCustomMessageHandler("xhr", function (x) {
  
  // 1. Create a new XMLHttpRequest object
  let xhr = new XMLHttpRequest();

  // 2. Configure it: GET-request for the URL
  let url = "https://raw.githubusercontent.com/stla/bigjson/main/bigjson.json";
  xhr.open("GET", url);

  // 3. Send the request over the network
  xhr.send();

  // 4. This will be called after the response is received
  let response; // variable to store the downloaded file
  xhr.onload = function () {
    if (xhr.status != 200) {
      // analyze HTTP status of the response
      alert(`Error ${xhr.status}: ${xhr.statusText}`); // e.g. 404: Not Found
    } else {
      // show the result in the console
      console.log(`Done, got ${xhr.response.length} bytes`); // response is the server response
      // store the response 
      // we don't send the response to Shiny here because it would block the progress bar
      response = xhr.response;
    }
  };

  let i = 1000; // this is used to slow down the communication with Shiny
  let totalSize = 100293197; // the size of the downloaded file
  let nsteps = 5; // number of steps for the progress bar
  let threshold = 1 / nsteps;
  xhr.onprogress = function (event) {
    if (event.lengthComputable) { // if available, totalSize is not needed
                                  // because it is given in event.total
      let ratio = event.loaded / event.total;
      if (ratio >= threshold) {
        setTimeout(function () {
          Shiny.setInputValue("received", ratio);
          if (ratio === 1) { // send response to Shiny
            Shiny.setInputValue("download", response);
          }
        }, i);
        threshold += 1 / nsteps;
      }
    } else { // event.total is not available
      let ratio = event.loaded / totalSize;
      if (ratio >= threshold) {
        setTimeout(function () {
          Shiny.setInputValue("received", ratio);
          if (ratio === 1) { // send response to Shiny
            Shiny.setInputValue("download", response);
          }
        }, i);
        threshold += 1 / nsteps;
      }
    }
    i += 1000;
  };

  xhr.onerror = function () {
    alert("Request failed");
  };
  
});

我在这里下载了 Github 上托管的文件。

lengthComputable
事件的
onprogress
false
;这意味着这个事件并没有提供下载的总大小,然后我们必须手动输入。

应用程序:

library(shiny)

ui <- fluidPage(
  tags$head(tags$script(src = "xhr.js")),
  br(),
  actionButton("go", "Go"),
  br(),
  tags$h2("Response will appear here:"),
  verbatimTextOutput("response")
)

server <- function(input, output, session) {

  # progress bar
  progress <- NULL
  
  observeEvent(input$go, {
    progress <<- Progress$new(session, min = 0, max = 1)
    progress$set(message = "Download in progress")
    # trigger the download in JavaScript
    session$sendCustomMessage("xhr", TRUE)
  })
  
  # input$received contains the ratio of the download in progress
  observeEvent(input$received, {
    progress$set(value = input$received)
    if(input$received == 1) {
      progress$close()
    }
  })
  
  # input$download contains the downloaded file (here a JSON string)
  output$response <- renderPrint({
    req(input$download)
    substr(input$download, 1, 100)
  })

}

shinyApp(ui, server)

© www.soinside.com 2019 - 2024. All rights reserved.