```java
 public Result thenApplyWithContext(Function<Result, Result> fn) {
        this.thenApply(fn.compose(beforeContext).andThen(afterContext));
        // You may need to return a new Result instance representing the next async stage,
        // like thenApply will return a new CompletableFuture.
        return this;
    }
```

to

```java
```java
 public Result thenApplyWithContext(BiFunction<Throwable, Result> fn) {
        this.handle(fn);
        // You may need to return a new Result instance representing the next async stage,
        // like thenApply will return a new CompletableFuture.
        return this;
    }
```
```
*/
    String getAttachment(String key);
    /**
     * get attachment by key with default value.
     *
     * @return attachment value.
     */
    String getAttachment(String key, String defaultValue);
    void setAttachment(String key, String value);
    /**
     * Returns the specified {@code valueIfAbsent} when not complete, or
     * returns the result value or throws an exception when complete.
     *
     * @see CompletableFuture#getNow(Object)
     */
    Result getNow(Result valueIfAbsent);
    /**
     * Add a callback which can be triggered when the RPC call finishes.
     * <p>
     * Just as the method name implies, this method will guarantee the callback being triggered under the same context as when the call was started,
     * see implementation in {@link AsyncRpcResult#thenApplyWithContext(Function)}
     *
     * @param fn
     * @return
     */
    Result thenApplyWithContext(Function<Result, Result> fn);
    default CompletableFuture<Result> completionFuture() {
        return toCompletableFuture();
    }
}