diff --git a/core/tests/Drupal/FunctionalJavascriptTests/JSWebAssert.php b/core/tests/Drupal/FunctionalJavascriptTests/JSWebAssert.php
index 0a3c49385547b6d3e3977740776cca5a0ab8b2d3..9db43998c6460663abe3982b568bfbcd2f7dde7c 100644
--- a/core/tests/Drupal/FunctionalJavascriptTests/JSWebAssert.php
+++ b/core/tests/Drupal/FunctionalJavascriptTests/JSWebAssert.php
@@ -12,7 +12,6 @@
 use PHPUnit\Framework\Constraint\IsNull;
 use PHPUnit\Framework\Constraint\LogicalNot;
 use WebDriver\Exception;
-use WebDriver\Exception\CurlExec;
 
 // cspell:ignore interactable
 
@@ -156,18 +155,7 @@ public function waitForText($text, $timeout = 10000) {
    *   The result of $callback.
    */
   private function waitForHelper(int $timeout, callable $callback) {
-    WebDriverCurlService::disableRetry();
-    $wrapper = function (Element $element) use ($callback) {
-      try {
-        return call_user_func($callback, $element);
-      }
-      catch (CurlExec $e) {
-        return NULL;
-      }
-    };
-    $result = $this->session->getPage()->waitFor($timeout / 1000, $wrapper);
-    WebDriverCurlService::enableRetry();
-    return $result;
+    return $this->session->getPage()->waitFor($timeout / 1000, $callback);
   }
 
   /**
diff --git a/core/tests/Drupal/Tests/DocumentElement.php b/core/tests/Drupal/Tests/DocumentElement.php
index d7b1617f254f363d5a8c6d3a5d3d13a0433a9a74..5d6def89cb5604be9ce6f9529de1c46fd13215a4 100644
--- a/core/tests/Drupal/Tests/DocumentElement.php
+++ b/core/tests/Drupal/Tests/DocumentElement.php
@@ -7,7 +7,10 @@
 namespace Drupal\Tests;
 
 use Behat\Mink\Driver\BrowserKitDriver;
+use Behat\Mink\Element\Element;
 use Behat\Mink\Element\TraversableElement;
+use Drupal\FunctionalJavascriptTests\WebDriverCurlService;
+use WebDriver\Exception\CurlExec;
 
 /**
  * Document element.
@@ -85,4 +88,31 @@ public function getText() {
     return parent::getText();
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function waitFor($timeout, $callback) {
+    // Wraps waits in a function to catch curl exceptions to continue waiting.
+    WebDriverCurlService::disableRetry();
+    $count = 0;
+    $wrapper = function (Element $element) use ($callback, &$count) {
+      $count++;
+      try {
+        return call_user_func($callback, $element);
+      }
+      catch (CurlExec $e) {
+        return NULL;
+      }
+    };
+    $result = parent::waitFor($timeout, $wrapper);
+    if (!$result && $count < 2) {
+      // If the callback or the system is really slow, then it might have only
+      // fired once. In this case it is better to trigger it once more as the
+      // page state has probably changed while the callback is running.
+      return call_user_func($callback, $this);
+    }
+    WebDriverCurlService::enableRetry();
+    return $result;
+  }
+
 }