private void scan0(Scan scan, ScanResultConsumer consumer) {
    Span span = null;
    try (AsyncTableResultScanner scanner = rawTable.getScanner(scan)) {
      span = scanner.getSpan();
      try (Scope ignored = span.makeCurrent()) {
        if (scan.isScanMetricsEnabled()) {
          consumer.onScanMetricsCreated(scanner.getScanMetrics());
        }
        for (Result result; (result = scanner.next()) != null;) {
          if (!consumer.onNext(result)) {
            break;
          }
        }
        consumer.onComplete();
      }
    } catch (IOException e) {
      try (Scope ignored = span.makeCurrent()) {
        consumer.onError(e);
      }
    }
  }
Just a wrapper of {@link RawAsyncTableImpl}. The difference is that users need to provide a
thread pool when constructing this class, and the callback methods registered to the returned
{@link CompletableFuture} will be executed in this thread pool. So usually it is safe for users
to do anything they want in the callbacks without breaking the rpc framework.
hbase-client/src/main/java/org/apache/hadoop/hbase/client/AsyncTableImpl.java