# 3.4.一切都是Handler

    Handler是Vert.x中最常见的一种**行为**结构，也是Vert.x中为什么最小JDK支持为8.0的主要原因——没错，它使用了JDK 8.0引入的lambda语法，您在官方文档中也许看到最多的代码就如下：

```java
vertx.setPeriodic(1000, id -> {
  // This handler will get called every second
  System.out.println("timer fired!");
});
```

    从此处开始，您就打开了Handler的大门，它的格式类似JS中常用的**回调**函数，也是前文提到的Vert.x中**启动周期**和**请求周期**的一个分水岭，在上述示例中，内部打印语句并不会立即执行，您可以理解`vertx.setPeriodic`在部署代码，部署好之后每隔一秒lambda函数内的打印语句会执行一次，这种思路充斥着整个Vert.x官方教程，如果单纯依靠这种写法也容易陷入代码中的**回调地狱**，本章节我会带领大家一起感受Handler的各种玩法。

    使用Handler之前，我们先看看Vert.x中的基本定义，理解了基本定义后，回过头来看Handler的基本用法会更加得心应手，读者需要牢记几个思考基础：

* Handler在主代码过程中通常只用于**绑定**（函数的延迟调用原理），并不会执行函数内部代码。
* Handler的内部代码会在**满足条件**时执行，一般是触发式的（这点很重要，您的**断点**打在什么位置是最需要您理解的）。
* Vert.x中大部分Handler是异步调用，读者需要区分**延迟调用**和**异步调用**，二者在某些地方是等价而在某些地方是不等价的，Vert.x中的异步调用通常会追加`AsyncResult`作数据的封装容器。

## 1. Handler接口

    Handler的主接口定义如下：

```java
@FunctionalInterface
public interface Handler<E> {

  /**
   * Something has happened, so handle it.
   *
   * @param event  the event to handle
   */
  void handle(E event);
}
```

    它是一个函数式接口（`@FunctionalInterface`修饰），基本接口十分简单，此处我们讨论下这个方法背后的设计。在整个Vert.x中，实现了这个接口的类不少，您可以将它理解成Vert.x框架中行为的**神经系统**，由于这个方法的返回值是`void`，意味着它可以支持异步也可支持同步，Vert.x内部的核心系统基础实际是**回调**模式，您可以在该方法的实现中直接书写类似：

```java
    @Override
    public void mount(final Route route, final RRecord record) {
        route.handler(res -> {
            // 异步回调：Handler内部代码，该代码无返回值
        });
        route.blockingHandler(res -> {
            // 同步回调
        })
    }
```

    遵循Vert.x的**黄金法则**，如果您要绑定同步的Handler，则尽可能找到类似`blockingHandler`的方法来绑定，该绑定内部由Vert.x执行调度，不会让您的程序出现阻塞；您若直接在`handler`方法中绑定同步代码，很有可能导致线程阻塞问题；若您的执行代码已经处理过**同步转异步**的操作，则可无视上述法则——**禁止直接绑定同步代码**依然是在Vert.x开发过程中牢记的法则。

    所以Handler接口的实现类大部分执行方式如下图（参考上边例子）：

![](/files/TQ6WDFqkVhirjBtZ7TQG)

    上图中Route对象就是编程过程中调用`handler`的对象，而此时执行的**绑定**（并不执行Handler内部代码），即Vert.x中Web流程的**启动周期**；而下边触发事件时的虚线数据流才会执行Handler的内部代码，即Vert.x中Web流程的**请求周期**，两部分代码不是同时执行的，这一点需读者牢记。

    比较有意思的是，如果调用`blockingHandler`执行了同步绑定，这些看似在Route对象内部的代码最终都会执行`Context`上下文对象中的同步调度块，而不在组件内部，整个Vert.x框架中的大部分组件都基于此原则——大部分调度代码最终由`Context`统一完成。读者可以直接跟踪`route.blockingHandler`的源代码如下：

```java
    // 代码文件：io.vertx.core.impl.ContextImpl
    PoolMetrics metrics = workerPool.metrics();
    Object queueMetric = metrics != null ? metrics.submitted() : null;
    Promise<T> promise = context.promise();
    Future<T> fut = promise.future();
    try {
      Runnable command = () -> {
        Object execMetric = null;
        if (metrics != null) {
          execMetric = metrics.begin(queueMetric);
        }
        context.dispatch(promise, f -> {
          try {
            blockingCodeHandler.handle(promise);
          } catch (Throwable e) {
            promise.tryFail(e);
          }
        });
        if (metrics != null) {
          metrics.end(execMetric, fut.succeeded());
        }
      };
      Executor exec = workerPool.executor();
      if (queue != null) {
        queue.execute(command, exec);
      } else {
        exec.execute(command);
      }
    } catch (RejectedExecutionException e) {
      // Pool is already shut down
      if (metrics != null) {
        metrics.rejected(queueMetric);
      }
      throw e;
    }
    return fut;
```

    细心的读者会发现，Vert.x整个框架中的组件在初始化时都包含了初始化组件的API函数，该函数的参数签名一般是`(Vertx,JsonObject)`格式或`(Vertx,Options)`格式，Vertx实例的引用就是通过它传入到组件内部，让所有组件共享的。阅读上述源代码之前，您需回顾一下两个基本概念：**函数引用**和**函数调用**：

* **函数引用**：通常构造如：`var fn = xxx`，构造出来的`fn`本身是一个函数，如Java8之后的`java.util.function.Function`对象，在构造该函数时，您只是使用`fn`变量获取了一个**函数引用**，而此时**函数没有执行**。
* **函数调用**：这个概念最简单，就是我们通常说的方法调用。

    代码中的`blockingCodeHandler`就是一个函数引用，它的类型就是本小节提到的`Handler`，所以在下边这行代码之前，函数内部的**绑定**代码都不会运行：

```java
blockingCodeHandler.handle(promise);
```

    如果您区分了函数引用和函数调用的概念，理解Handler就轻而易举了，最后说明一点：是否异步执行取决于函数调用时的上下文（代码块），而不是函数引用的上下文，在Vert.x环境中对比如下：

```java
// 假设此处的 data 是普通Java对象，并非Vert.x中的Future或Promise
// 同步执行
blockingCodeHandler.handle(data);

// 异步执行，等待当前future完成之后
future.onComplete(res -> {
    blockingCodeHandler.handle(data);
})
```

    上述概念代码就是Handler的核心原理，至于`blockingCodeHandler`引用函数的内部代码是同步还是异步取决于函数内部的实现代码，回到本小节最初提到的第三点：如果您已经拿到了一个**函数引用**，那么该函数就具备了**延迟调用**的功能，因为这个函数会在您触发它的时候执行（如执行`handler.handle(xx)`），而触发之前环境中只是维持了该函数引用；若函数内代码本身是同步的，那么只是单纯的**延迟调用**，若函数内代码是异步的，那么它才是**异步调用**。之所以会将延迟调用和异步调用混淆，是因为二者具备极高的相似性，它们都是延迟得到执行结果，但从原理上分析，二者存在本质的区别。

## 2. AsyncResult接口

> 看完了Handler之后，我们继续屠城。

    如果说Handler的设计是用来区分**函数引用**和**函数调用**，那么另外一个接口`io.vertx.core.AsyncResult`就是用来区分**同步调用**和**异步调用**，该接口的定义如下：

```java
public interface AsyncResult<T> {
    // 其他方法定义
}
```

    理解接口之前先看一个图示：

![](/files/Byd8VsHJbJNoDcY4dWo4)

    上图描述了`AsyncResult`接口中所有API的概念图，它是一个典型的Monad容器，容器内数据类型为`T`，包含了两种状态：**成功**和**失败**，每种状态提供了**判断**函数以及**读取**数据的函数，**失败**时读取的数据为一个Java语言中的`Throwable`对象；由于`map`和`otherwise`是绑定的函数，函数中实现同步或异步由开发人员自定。

    参考下边的简单代码：

```java
package io.vertx.up._03.handler;

import io.vertx.core.Future;

public class AsyncMain {

    public static void main(final String[] args) {
        first().map(AsyncMain::add5).onSuccess(res -> {
            if (res.succeeded()) {
                System.out.println(res.result() + "," + Thread.currentThread().getName());
            }
        });
        System.out.println("-------------------------");
        first().map(AsyncMain::addx).onFailure(res -> {
            res.printStackTrace();
            System.out.println("Error, " + Thread.currentThread().getName());
        });
        System.out.println("-------------------------");
        first().map(AsyncMain::addDefaultX)
            .otherwise(AsyncMain::addDefault).onComplete(res -> {
                if (res.succeeded()) {
                    System.out.println("Default, " + res.result());
                } else {
                    System.out.println("Other Error");
                }
            });
    }

    private static Integer addDefault(final Throwable error) {
        error.printStackTrace();
        System.out.println("Default, 3 " + Thread.currentThread().getName());
        return 3;
    }

    private static Integer addDefaultX(final int seed) {
        throw new RuntimeException("Default, " + seed);
    }

    private static Future<Integer> addx(final int seed) {
        throw new RuntimeException("Err, " + seed);
    }

    private static Future<Integer> add5(final int seed) {
        System.out.println("Seed, " + seed + " " + Thread.currentThread().getName());
        return Future.succeededFuture(5 + seed);
    }

    private static Future<Integer> first() {
        System.out.println("First, 10 " + Thread.currentThread().getName());
        return Future.succeededFuture(10);
    }
}
```

    运行该代码您会得到如下信息输出：

```shell
First, 10 main
Seed, 10 main
15,main
-------------------------
First, 10 main
Error, main
-------------------------
First, 10 main
Default, 3 main
java.lang.RuntimeException: Err, 10
	at io.vertx.up._03.handler.AsyncMain.addx(AsyncMain.java:40)
	at io.vertx.core.impl.future.Mapping.onSuccess(Mapping.java:35)
	at io.vertx.core.impl.future.FutureBase.emitSuccess(FutureBase.java:60)
	at io.vertx.core.impl.future.SucceededFuture.addListener(SucceededFuture.java:88)
	at io.vertx.core.impl.future.FutureBase.map(FutureBase.java:108)
	at io.vertx.core.impl.future.SucceededFuture.map(SucceededFuture.java:27)
	at io.vertx.up._03.handler.AsyncMain.main(AsyncMain.java:14)
java.lang.RuntimeException: Default, 10
	at io.vertx.up._03.handler.AsyncMain.addDefaultX(AsyncMain.java:36)
	at io.vertx.core.impl.future.Mapping.onSuccess(Mapping.java:35)
	at io.vertx.core.impl.future.FutureBase.emitSuccess(FutureBase.java:60)
	at io.vertx.core.impl.future.SucceededFuture.addListener(SucceededFuture.java:88)
	at io.vertx.core.impl.future.FutureBase.map(FutureBase.java:108)
	at io.vertx.core.impl.future.SucceededFuture.map(SucceededFuture.java:27)
	at io.vertx.up._03.handler.AsyncMain.main(AsyncMain.java:19)
Default, 3
```

    上述代码中有三个例子：

1. 第一个例子最简单，全程没有任何异常抛出，所以最终调用了`onSuccess`方法打印了最终结果，它的数据变化如：`10 -> 15`，最终输出结果。
2. 第二个例子中，强制性在`addx`方法内抛出了一个异常，最终调用`onFailure`方法捕捉异常发生后的情况。
3. 第三个例子中绑定了otherwize函数，并返回了同步结果`Integer`，最终调用`onComplete`方法来捕捉结果。

    开发人员在使用`AsyncResult<T>`一定要注意此处的`T`类型，在关注类型时，`T`类型本身很简单的，比较复杂的是**转换之后的类型**。代码中的第三个例子在调用`map`时绑定的是同步函数，也就意味着最终转换出来的结果类型已经是`Integer`，此时如果您直接使用`onSuccess`，那么都不用执行下边的检查代码：

```java
    if (res.succeeded()) {
        System.out.println("Default, " + res.result());
    } else {
        System.out.println("Other Error");
    }
```

    此处根据`map/otherwise`两个方法的定义来理解：

```java
    default <U> AsyncResult<U> map(Function<T, U> mapper)
    default AsyncResult<T> otherwise(Function<Throwable, T> mapper)
```

    两个函数在执行过程中，不会去理睬返回值（`map`中的`T`，`otherwise`中的`U`）是一个异步结构还是一个同步结构，通常开发人员可以选择两种方式捕捉结果：

* `onSuccess/onFailure`配对，这种模式下函数参数就已经是内部结果`T`或`U`了。
* `onComplete`，这种模式下您拿到的其实是原始结果，它并没有执行任何数据提取操作。

    发现问题没？其实`map`和`otherwize`构造出来的实际是类似Java中lambda的`map`效果，它是一个**函数链**，并不是字面理解的**异步数据流**（我们所有的例子此时都还是同步的），如果您把第一个例子中的`onSuccess`改成`onComplete`，您会发现res的数据结构形如`AsyncResult<Future<Integer>>`，和期望的`AsyncResult<Integer>`不同，这就是`map`带来的效果——函数返回什么内容，那么它就解析成什么类型，并对此类型执行AsyncResult封装，至于该类型是同步还是异步，它不关心。`map`和`otherwize`构造了如下的一种数据结构：

![](/files/dI2nJwPDDEEgFrwdtCpF)

    所有的函数执行都会有**成功**和**失败**两种状态，而Java语言中的函数本身不支持**多返回值**，于是有了`AsyncResult<T>`对函数执行结果的数据和状态进行封装，如此，函数即使出现异常，也只会生成一个**失败**状态的`AsyncResult<T>`而不是以异常的方式抛出，这也遵循了函数式语言中Monad的特性，所以从这点意义上讲，`AsyncResult<T>`**本质**就是函数式编程中最子元的Monad结构，它最大的改动就是让您的函数可以返回**双态**。

    您也许会问，既如此，为何`AsyncResult<T>`的字面名字会叫**异步结果**？主要原因是`AsyncResult`在Vert.x中是以接口的形式定义，它的API形如Monad，而最终这个Monad的实现是同步还是异步取决于实现类，Vert.x中最常用的两个实现类是FutureImpl和PromiseImpl，这两货内部实现都是异步数据流，因此这个名字就实至名归了。

## 3. 再谈`Future<T>/Promise<T>`

    紧接前一章节提到的`AsyncResult<T>`，本章我们再来温习两个可爱的小宝贝：Future和Promise，只是这次我们换个视角，从整体结构来解读Vert.x中这部分内容：

### 3.1. 整体结构

> \[I]标记是接口，\[A]标记是抽象类。

![](/files/RpfoFDcbe8MZKppL4g0m)

    从结构图上可知：

* `Future`的类型是前文提到的`AsyncResult<T>`。
* `Promise`的类型则是`Handler<AsyncResult<T>>`。

    `Future<T>`接口定义中存在几个和`Handler`直接相关的方法：

```java
// 成功时回调
Future<T> onSuccess(Handler<T> handler)
// 失败时回调
Future<T> onFailure(Handler<Throwable> handler)
// 完成时回调（双态）
Future<T> onComplete(Handler<AsyncResult<T>> handler)
```

    这三个方法最早是没有的，3.x中原始版本最常用的方法是`setHandler`，该方法等价于`onComplete`，从`3.8`开始该方法被标记为**废弃**（`@Deprecated`），`4.x`之后就被移除了，这种设计是因为早期代码经常会是如下写法：

```java
future.setHandler(res -> {
    if(res.succeeded()){
        // 成功返回
    }else{
        // 失败返回
    }
})
```

    虽然这种写法会使得函数本身更趋近于**全函数**，但在开发过程中，我们的业务层面有时候只关心**单边**状态，如成功时如何，又或者失败时如何，如果一直使用`setHandler`绑定函数，那么系统中会到处充斥着上述结构的代码，这样会显得冗余，所以取而代之提供了`onSuccess`和`onFailure`的绑定模式。

    也就是说上述示例代码：

```java
first().map(AsyncMain::add5).onSuccess(res -> {
    if (res.succeeded()) {
        System.out.println(res.result() + "," + Thread.currentThread().getName());
    }
});
```

    也可直接取消`res.succeeded()`的判断，直接写成：

```java
first().map(AsyncMain::add5).onSuccess(res -> {
    System.out.println(res.result() + "," + Thread.currentThread().getName());
});
```

    此处牵涉一个简单的编程思维问题：Handler是否有必要处处都**检查**？这个问题可根据实际情况而定，我们在编程过程中通常会有**A**调用**B**的模式，是否**检查**取决于函数的内部实现，如果内部实现是**可信任**的，即使用了类似前文提到的`otherwise`方法绑定**异常**情况，那么就没有必要**检查**，而若内部实现会访问网络、数据库、文件系统等无法预知的资源，除了使用`try-catch`执行`Checked`异常转换，也可以让部分`Runtime`的异常抛出，丢给Handler来**检查**（`res.failed()`）。也许有人会说处处检查更完美，但完美的东西不一定实用，况且对代码本身的维护也是一种成本，所以根据分析，您就可以择优而从，因地制宜了。

### 3.2. 三态

    前文一直提到函数执行要么**成功**，要么**失败**，此处的**三态**如何讲呢？其实还有一种就是Handler中同时包含了成功和失败，因为`FutureBase`有三个直接子类，三者的统计如下：

| 类名              | 等价回调       | 访问域       | 说明        |
| --------------- | ---------- | --------- | --------- |
| FutureImpl      | onComplete | (default) | 同时支持两种状态。 |
| SucceededFuture | onSuccess  | public    | 成功时专用。    |
| FailedFuture    | onFailure  | public    | 失败时专用。    |

    由于FutureImpl类的访问域是default的，所以基本不会使用，但内部是如此设计，若您要理解Vert.x中的结构，不可越过它；在`3.8.x`版本之前，Future类有一个直接构造方法`Future.future()`，FutureImpl类就是为它量身打造的，而之后的版本进行了细分设计，该方法就被**废弃**了，取而代之的就是`Promise.promise()`。开发人员在使用Future中常用的代码如：

```java
// 成功
Future.succeededFuture(t);
// 失败
Future.failedFuture(throwable);
```

    上述两个方法会构造SucceededFuture实例和FailedFuture实例，这两个类的出现是**细粒度设计**的体现，二者一个职责是表示**成功**，另外一个职责就是表示**失败**，它们的区别如下：

1. 二者构造函数不同

   ```java
   // 成功
   public SucceededFuture(T result)
   // 失败
   public FailedFuture(Throwable t)
   ```
2. 置留的空方法不同，由于这两个类都是**单态**，所以对另外一种状态会存在忽略不计的情况，若您在书写时使用了反向**绑定**（如SucceededFuture调用了onFailure绑定）可能什么也不会发生，这也是开发过程中容易被忽视的点。

   ```java
   // SucceededFuture中的 onFailure 方法
   public Future<T> onFailure(Handler<Throwable> handler) {
       return this;
   }
   // FailedFuture中的 onSuccess 方法
   public Future<T> onSuccess(Handler<T> handler){
       return this;
   }
   ```

    FutureBase中有两个只有子类（`protected`域）才能访问的主方法`emitSuccess(T,Listener<T>)`和`emitFailure(Throwable,Listener<T>)`，这两个方法是整个Future/Promise的核心逻辑，FutureBase的构造函数如下：

```java
    // 未绑定Vert.x中的上下文对象Context
    FutureBase() {
        this(null);
    }
    // 绑定了Vert.x中的上下文对象Context
    FutureBase(ContextInternal context) {
        this.context = context;
    }
```

    到此处，相信读者对下边几个方法已经有答案了：

* Future什么时候是异步的，什么时候是同步的？
* Future和Promise的本质是什么？

    在Vert.x中，Future本身是同时支持**同步**和**异步**的，在同步模式中，它可以不和Context上下文环境产生任何关系，此种模式下可直接触发绑定的Handler实现**同步**转异步的操作（只是代码模式转成了异步，是否异步同样取决于绑定Handler的内部实现）；若Future和Context上下文环境相关联，那么它就具备了先天性的**异步**特征。

    Future和Promise不同的点是继承的接口，前者从`AsyncResult<T>`继承，后者则从`Handler<AsyncResult<T>>`继承。Future本质上是一种**数据结构**，您可以将它理解成**异步容器**，它将数据结果封装在容器内部，提供给**函数链**消费，而其本身就是标准的Monad结构；Promise本质上是一种**行为**，它在`3.8.x`之前都是不存在的，它是从**原版**的Future中剥离出来的。——二者实则是**异步数据**和**异步行为**的一种分离设计，各司其职。

    之所以一直对Future/Promise念念不忘，重复讲解，主因是二者不论在Vert.x内部还是Future风格的开发中随处可见，若您不掌握二者的用法，可能在开发过程中举步维艰。

## 4. 其他Handler

    上述提到的Handler是整个Vert.x中的Handler主结构，Vert.x中多数API都使用了该结构，到这里，相信您再去阅读Vert.x的源码就更加轻车熟路了，我们跟着目前所见之初瞅瞅它，让读者对**一切都是Handler**有更深入的印象。

### 4.1. Vertx初始化

    Vertx实例在初始化时支持两种模式：单机模式和集群模式，单机模式的启动是同步创建，集群模式则是异步实现，二者函数签名如：

```java
// VertxBuilder中的代码
// 单机模式
public Vertx vertx();
// 集群模式
public void clusteredVertx(Handler<AsyncResult<Vertx>> handler)
```

    于是就有了在初始化集群时的官方代码：

```java
/*
 * Vertx中的clusteredVertx方法内部封装如
 * new VertxBuilder(options).init().clusteredVertx(resultHandler);
 * VertxBuilder会优先读取配置并执行组件初始化，再调用上边提到的构造函数实例化Vertx
 */
VertxOptions options = new VertxOptions();
Vertx.clusteredVertx(options, res -> {
  if (res.succeeded()) {
    Vertx vertx = res.result();
    EventBus eventBus = vertx.eventBus();
    System.out.println("We now have a clustered event bus: " + eventBus);
  } else {
    System.out.println("Failed: " + res.cause());
  }
});
```

### 4.2. Verticle的部署

    Vertx实例从部署Verticle开始就已经是异步部署了，部署组件没有**同步模式**，它的API形如：

```java
// Future封装
Future<String> deployVerticle(Verticle verticle, DeploymentOptions options)
// Handler模式
void deployVerticle(Verticle verticle, DeploymentOptions options, 
                    Handler<AsyncResult<String>> completionHandler)
```

    官方有一段代码：

```java
// 标准模式
vertx.deployVerticle("com.mycompany.MyOrderProcessorVerticle", res -> {
    if (res.succeeded()) {
        System.out.println("Deployment id is: " + res.result());
    } else {
        System.out.println("Deployment failed!");
    }
});
// 小坑代码
DeploymentOptions options = new DeploymentOptions().setInstances(16);
vertx.deployVerticle("com.mycompany.MyOrderProcessorVerticle", options);
```

    之所以说此处代码是**小坑**是因为在部署Verticle组件时，我们本身就会等待部署结果，无关部署完成后去执行此操作，`Future<String>`的返回值并不代表代码本身不执行，而是随后会执行，执行完成待定。它提供了这样一种**视角**：一个函数返回`Future`，并不代表它不会执行，而是它不会立即执行，若您不关心返回值，则无需设置对应的Handler，当然也不需要调用`onSuccess/onFailure`执行绑定，等待结果即可。若您想要在执行完成后有对应操作，则必须设置Handler代码，Handler的另外一种本质就是类似JS中的**回调**，它采用了Callback风格的代码让您可以在**异步操作**执行完成之后再处理其他事情。图示如下：

![](/files/Mdwt90qPfY5U8zieuTwi)

    图中的灰色区域是开发人员无法触碰的区域，主代码一旦调用了`deployVerticle之后`，它的执行就不受主代码管控了，能等待它执行结果的地方就只有异步回调代码了，而主代码中的`return`操作在此时不起任何作用，这和JS中的异步如出一辙，而灰色区域实则是开发过程中的盲区。

### 4.3. Verticle启动

    我们一般写Verticle代码，只会去重写`start()`方法，通常写法如：

```java
public class OptionVerticle extends AbstractVerticle {

    @Override
    public void start() {
        final HttpServer server = this.vertx.createHttpServer();
        server.requestHandler(handler -> {
            System.out.println(Thread.currentThread().getName());
            handler.response()
                    .putHeader("content-type", "text/plain")
                    .end("Hello Direct Server!");
        });
        server.listen(1023, res -> {
            System.out.println("Server Started");
        });
    }
}
```

    而AbstractVerticle中包含了异步方法函数如下：

```java
  @Override
  public void start(Promise<Void> startPromise) throws Exception {
      start();
      startPromise.complete();
  }
```

    这里有个小细节：异步实现中只是单纯调用了`start()`函数，如果该函数本身包含了**异步**行为，那么这个行为您可以在函数内部书写回调，Verticle组件本身无法捕捉回调内容，也就是说，Verticle的默认异步启动只是发起了**异步请求**，并没等待您的start()方法执行完成，若要让您的start()方法在执行完成后再标识Verticle部署完成（示例中等待监听结果），您可以重写`start(Promise<Void>)`而不是`start()`，代码修改如下：

```java
    @Override
    public void start(final Promise<Void> startPromise) throws Exception {
        final HttpServer server = this.vertx.createHttpServer();
        server.requestHandler(handler -> {
            System.out.println(Thread.currentThread().getName());
            handler.response()
                .putHeader("content-type", "text/plain")
                .end("Hello Direct Server!");
        });
        server.listen(1023, res -> {
            if (res.succeeded()) {
                System.out.println("Server Started");
                startPromise.complete();
            } else {
                startPromise.fail(res.cause());
            }
        });
    }
```

    如此：Verticle的部署完成就一定会发生在HttpServer监听成功之后了！所幸的是大部分情况下，部署Verticle是无需如此操作，异步部署已经是很优秀的解决办法了。配合下边主代码：

```java
public class AsyncLauncher {

    public static void main(final String[] args) {
        // 选择单点模式
        final Launcher launcher = new SingleLauncher();

        launcher.start(vertx -> {
            // 发布
            vertx.deployVerticle(OptionVerticle::new,
                new DeploymentOptions().setInstances(1), res -> {
                    if (res.succeeded()) {
                        System.out.println("Verticle Completed");
                    }
                });
        });
    }
}
```

    打印结果：

```shell
# 同步启动打印结果，重写 start(Promise)
Server Started
Verticle Completed

# 异步启动打印结果，重写 start()
Verticle Completed
Server Started
```

    同步打印结果是固定的，每次打印结果都会按顺序输出，异步打印结果理论上讲由于Verticle的start()方法要耗费时间，所以`Verticle Completed`先打印，但如果小概率出现了Verticle组件先执行完，那么有可能`Server Started`也先打印，这点读者尤其注意，两种打印结果前者是固定顺序，后者并不是固定顺序，而是无序的，您理解了这点，那么对异步数据流就有了更深的理解。

## 5. 总结

    本章主要讲解了Vert.x中Handler部分主体内容，也解析了其常用结构和用法，在涉入Vert.x的Web开发之前，这里属于**必经之路**。您若仔细去阅读Vert.x的源代码，会在整个框架内部看到很多类似`Handler<AsyncResult<T>>`的定义，阅读了本章内容，那么您更容易理解Handler，面对很多API的调用以及相关写法，就不会疑惑了。


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://lang-yu.gitbook.io/vert.x/03-index/03-4-handler.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
