Search code examples
javagridvaadin

Grid content loads "synchronously" when it should not


I am using vaadin grid with push enabled and an asynchronous method to fetch some data. What I would expect is that the data that is already present in the entity is loaded instantly and the data that is loaded using an asynchronous method is loaded using callback. What currently happens is that the outer grid structure is loaded but the content is loaded after a longer period of time all at once. Here is sample code:

private void setupGrid() {
  // String grid for illustration
  Grid<String> grid = new Grid<>();
  grid.addComponentColumn(this::getIconColumn).setHeader("Name");

  // other entity items would be displayed here
  // grid.addComponentColumn(this::getMainColumn).setHeader("Other");

  grid.setItems("2800-player-who-returned-10000-years-later",
    "2633-duke-pendragon", "8713-dungeon-reset");

  // this allows the outer grid structure to be displayed while not blocking UI
  // but content is still loaded "synchronously" - all at once, not one by one
  grid.getDataCommunicator().enablePushUpdates(Executors.newCachedThreadPool());

  add(grid);
}

private Image getIconColumn(String entityName) {
  Image image = new Image("", "");
  image.setHeight("150px");
  image.setWidth("100px");

  UI ui = UI.getCurrent();
  asyncLoadIcon(entityName)
      .addCallback(
          result -> ui.access(() -> image.setSrc(result)),
          err -> ui.access(() -> Notification.show("Failed to parse icon for " + entityName))
      );

  return image;
}

And finally the async method with Thread.sleep(3000) for illustration purposes.

@Async
public ListenableFuture<String> asyncLoadIcon(String entityName) {
  try {
    // some real code

    Thread.sleep(3000);
    return AsyncResult.forValue(entityname);

  } catch (IOException | InterruptedException e) {
    log.error(e);
  }
  return AsyncResult.forExecutionException(new RuntimeException("Error"));
}

Solution

  • I have found a solution to my problem on the Vaadin documentantion page for Server Push. What I needed to add, was that in the getIconColumn() method the asyncLoadIcon() action with callback is started in a separate Thread each time. This has resulted in me getting the grid outline instantly, along with the easily generated content and some empty image icons. The image icons are then gradually filled in when the async method finishes. Here is the update code:

      private Image getIconColumn(MangaEntity entity) {
        Image image = new Image("", "");
        image.setHeight("150px");
        image.setWidth("100px");
    
        UI ui = UI.getCurrent();
        new Thread(() -> rsCrawler.asyncLoadIcon(entity.getUrlName())
            .addCallback(
                result -> ui.access(() -> image.setSrc(result)),
                err -> ui.access(() -> Notification.show("Failed to parse icon for " + entity.getName()))
            )).start();
    
        return image;
      }