# 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接口的实现类大部分执行方式如下图（参考上边例子）：

![](https://1702219450-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-Lv070Tm3mzVeCv0ENzP%2Fuploads%2Fgit-blob-960da9ccb5b27519ac784056e2d34aecf3a58af9%2F2021-10-06-11-38-36.jpg?alt=media)

    上图中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> {
    // 其他方法定义
}
```

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

![](https://1702219450-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-Lv070Tm3mzVeCv0ENzP%2Fuploads%2Fgit-blob-166bbc1a2c165304aeeaa5c6ab414dfb43b80f0f%2F2021-10-09-10-37-59.jpg?alt=media)

    上图描述了`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`构造了如下的一种数据结构：

![](https://1702219450-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-Lv070Tm3mzVeCv0ENzP%2Fuploads%2Fgit-blob-2b02ead561ee51acb3aacf50a9e2db488bbf9741%2F2021-10-09-11-30-13.jpg?alt=media)

    所有的函数执行都会有**成功**和**失败**两种状态，而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]标记是抽象类。

![](https://1702219450-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-Lv070Tm3mzVeCv0ENzP%2Fuploads%2Fgit-blob-a2af19804cef1584a1426702e0fc62808e42e914%2F2021-10-10-07-21-22.jpg?alt=media)

    从结构图上可知：

* `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风格的代码让您可以在**异步操作**执行完成之后再处理其他事情。图示如下：

![](https://1702219450-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-Lv070Tm3mzVeCv0ENzP%2Fuploads%2Fgit-blob-540e7a4d6720a1f251ae23d43e30dd84a7546616%2F2021-10-10-09-08-36.jpg?alt=media)

    图中的灰色区域是开发人员无法触碰的区域，主代码一旦调用了`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的调用以及相关写法，就不会疑惑了。
