Bug 1381876 - Introduce TimedPromise. r=automatedtester
authorAndreas Tolfsen <ato@sny.no>
Tue, 01 Aug 2017 18:28:13 +0100
changeset 422579 4f1c4c9fd451dadf13e46c4f2de99300adf6565c
parent 422578 66ce6c8cd0538380cb20e8495d85ab25c7984bf9
child 422580 ec8c6742603a676e21a86194cde43a594fbc2773
push id7761
push userjlund@mozilla.com
push dateFri, 15 Sep 2017 00:19:52 +0000
treeherdermozilla-beta@c38455951db4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersautomatedtester
bugs1381876
milestone57.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1381876 - Introduce TimedPromise. r=automatedtester This introduces a specialisation of the well-known Promise that can time out after a set limit, causing the promises' "reject" callback to be invoked. The TimedPromise object represents the timed, eventual completion (or failure) of an asynchronous operation, and its resulting value. In contrast to a regular Promise, it times out after a set timeframe. MozReview-Commit-ID: Rb3POsPYeT
testing/marionette/wait.js
--- a/testing/marionette/wait.js
+++ b/testing/marionette/wait.js
@@ -1,27 +1,32 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
 
-Cu.import("chrome://marionette/content/error.js");
+const {
+  error,
+  TimeoutError,
+} = Cu.import("chrome://marionette/content/error.js", {});
 
-this.EXPORTED_SYMBOLS = ["wait"];
+this.EXPORTED_SYMBOLS = ["wait", "TimedPromise"];
 
 /**
  * Poll-waiting utilities.
  *
  * @namespace
  */
 this.wait = {};
 
+const {TYPE_ONE_SHOT, TYPE_REPEATING_SLACK} = Ci.nsITimer;
+
 /**
  * @callback Condition
  *
  * @param {function(*)} resolve
  *     To be called when the condition has been met.  Will return the
  *     resolved value.
  * @param {function} reject
  *     To be called when the condition has not been met.  Will cause
@@ -99,19 +104,74 @@ wait.until = function(func, timeout = 20
         }
       }).catch(reject);
     };
 
     // the repeating slack timer waits |interval|
     // before invoking |evalFn|
     evalFn();
 
-    timer.init(evalFn, interval, Ci.nsITimer.TYPE_REPEATING_SLACK);
+    timer.init(evalFn, interval, TYPE_REPEATING_SLACK);
 
-  // cancel timer and propagate result
   }).then(res => {
     timer.cancel();
     return res;
   }, err => {
     timer.cancel();
     throw err;
   });
 };
+
+/**
+ * The <code>TimedPromise</code> object represents the timed, eventual
+ * completion (or failure) of an asynchronous operation, and its
+ * resulting value.
+ *
+ * In contrast to a regular {@link Promise}, it times out after
+ * <var>timeout</var>.
+ *
+ * @param {Condition} func
+ *     Function to run, which will have its <code>reject</code>
+ *     callback invoked after the <var>timeout</var> duration is reached.
+ *     It is given two callbacks: <code>resolve(value)</code> and
+ *     <code>reject(error)</code>.
+ * @param {timeout=} [timeout=1500] timeout
+ *     <var>condition</var>'s <code>reject</code> callback will be called
+ *     after this timeout.
+ * @param {Error=} [throws=TimeoutError] throws
+ *     When the <var>timeout</var> is hit, this error class will be
+ *     thrown.  If it is null, no error is thrown and the promise is
+ *     instead resolved on timeout.
+ *
+ * @return {Promise.<*>}
+ *     Timed promise.
+ */
+function TimedPromise(fn, {timeout = 1500, throws = TimeoutError} = {}) {
+  const timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
+
+  return new Promise((resolve, reject) => {
+    // Reject only if |throws| is given.  Otherwise it is assumed that
+    // the user is OK with the promise timing out.
+    let bail = res => {
+      if (throws !== null) {
+        let err = new throws();
+        reject(err);
+      } else {
+        resolve();
+      }
+    };
+
+    timer.initWithCallback({notify: bail}, timeout, TYPE_ONE_SHOT);
+
+    try {
+      fn(resolve, reject);
+    } catch (e) {
+      reject(e);
+    }
+
+  }).then(res => {
+    timer.cancel();
+    return res;
+  }, err => {
+    timer.cancel();
+    throw err;
+  });
+}