@Override
  public <R> void batchCallback(List<? extends Row> actions, Object[] results,
    Batch.Callback<R> callback) throws IOException, InterruptedException {
    ConcurrentLinkedQueue<ThrowableWithExtraContext> errors = new ConcurrentLinkedQueue<>();
    CountDownLatch latch = new CountDownLatch(actions.size());
    AsyncTableRegionLocator locator = conn.getRegionLocator(getName());
    List<CompletableFuture<R>> futures = table.<R> batch(actions);
    for (int i = 0, n = futures.size(); i < n; i++) {
      final int index = i;
      FutureUtils.addListener(futures.get(i), (r, e) -> {
        if (e != null) {
          errors.add(new ThrowableWithExtraContext(e, EnvironmentEdgeManager.currentTime(),
            "Error when processing " + actions.get(index)));
          if (!ArrayUtils.isEmpty(results)) {
            results[index] = e;
          }
          latch.countDown();
        } else {
          if (!ArrayUtils.isEmpty(results)) {
            results[index] = r;
          }
          FutureUtils.addListener(locator.getRegionLocation(actions.get(index).getRow()),
            (l, le) -> {
              if (le != null) {
                errors.add(new ThrowableWithExtraContext(le, EnvironmentEdgeManager.currentTime(),
                  "Error when finding the region for row "
                    + Bytes.toStringBinary(actions.get(index).getRow())));
              } else {
                callback.update(l.getRegion().getRegionName(), actions.get(index).getRow(), r);
              }
              latch.countDown();
            });
        }
      });
    }
    latch.await();
    if (!errors.isEmpty()) {
      throw new RetriesExhaustedException(errors.size(),
        errors.stream().collect(Collectors.toList()));
    }
  }
The table implementation based on {@link AsyncTable}.
hbase-client/src/main/java/org/apache/hadoop/hbase/client/TableOverAsyncTable.java