Repository: eclipse-vertx/vert.x

Problem statement:
JsonParser: endHandler called before exceptionHandler
### Version

Vert.x 4.3.8

### Context

JsonParser  may now report errors with a call to exceptionHandler after endHandler is called.

In 4.3.7 and earlier releases endHandler was guaranteed to be last callback if the document was valid.

With new behavior it is impossible to determine if a document is valid unless just waiting for events to propagate.

Related:

#4596 
#4597 

### Do you have a reproducer?

```java
@Test
  // 4.3.8 [end][exception]
  // 4.3.7 [exception]
  public void testErrorDoc(TestContext context) {
    var async = context.async();
    StringBuilder s = new StringBuilder();
    JsonParser jsonParser = JsonParser.newParser();
    jsonParser.endHandler(x -> s.append("[end]"));
    jsonParser.exceptionHandler(x -> s.append("[exception]"));
    jsonParser.write(Buffer.buffer("{"));
    jsonParser.end();
    Vertx.vertx().setTimer(10, x -> {
      context.assertEquals("[end][exception]", s.toString());
      async.complete();
    });
  }
```



Hints:

Patch:
diff --git a/src/main/java/io/vertx/core/parsetools/impl/JsonParserImpl.java b/src/main/java/io/vertx/core/parsetools/impl/JsonParserImpl.java
--- a/src/main/java/io/vertx/core/parsetools/impl/JsonParserImpl.java
+++ b/src/main/java/io/vertx/core/parsetools/impl/JsonParserImpl.java
@@ -288,6 +288,7 @@ private void checkPending() {
         }
         if (ended) {
           if (pending.isEmpty()) {
+            checkExceptions();
             Handler<Void> handler = endHandler;
             endHandler = null;
             if (handler != null) {


Test patch:
diff --git a/src/test/java/io/vertx/core/parsetools/JsonParserTest.java b/src/test/java/io/vertx/core/parsetools/JsonParserTest.java
--- a/src/test/java/io/vertx/core/parsetools/JsonParserTest.java
+++ b/src/test/java/io/vertx/core/parsetools/JsonParserTest.java
@@ -96,21 +96,30 @@ public void testParseEmptyArray() {
   }
 
   @Test
-  public void parseUnfinished() {
-    Buffer data = Buffer.buffer("{\"un\":\"finished\"");
+  public void parseUnfinishedThrowingException() {
+    StringBuilder events = new StringBuilder();
+    JsonParser parser = JsonParser.newParser();
+    parser.handler(e -> events.append("json,"));
+    parser.endHandler(v -> events.append("end,"));
+    parser.handle(Buffer.buffer("{\"un\":\"finished\""));
     try {
-      JsonParser parser = JsonParser.newParser();
-      parser.handle(data);
       parser.end();
       fail();
     } catch (DecodeException expected) {
     }
+    assertEquals("json,json,", events.toString());
+  }
+
+  @Test
+  public void parseUnfinishedExceptionHandler() {
+    StringBuilder events = new StringBuilder();
     JsonParser parser = JsonParser.newParser();
-    List<Throwable> errors = new ArrayList<>();
-    parser.exceptionHandler(errors::add);
-    parser.handle(data);
+    parser.handler(e -> events.append("json,"));
+    parser.endHandler(v -> events.append("end,"));
+    parser.exceptionHandler(e -> events.append("exception,"));
+    parser.handle(Buffer.buffer("{\"un\":\"finished\""));
     parser.end();
-    assertEquals(1, errors.size());
+    assertEquals("json,json,exception,end,", events.toString());
   }
 
   @Test