diff options
Diffstat (limited to 'devtools/shared/async-utils.js')
-rw-r--r-- | devtools/shared/async-utils.js | 107 |
1 files changed, 107 insertions, 0 deletions
diff --git a/devtools/shared/async-utils.js b/devtools/shared/async-utils.js new file mode 100644 index 000000000..932f74d02 --- /dev/null +++ b/devtools/shared/async-utils.js @@ -0,0 +1,107 @@ +/* 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"; + +/** + * Helpers for async functions. Async functions are generator functions that are + * run by Tasks. An async function returns a Promise for the resolution of the + * function. When the function returns, the promise is resolved with the + * returned value. If it throws the promise rejects with the thrown error. + * + * See Task documentation at https://developer.mozilla.org/en-US/docs/Mozilla/JavaScript_code_modules/Task.jsm. + */ + +var {Task} = require("devtools/shared/task"); +var Promise = require("promise"); + +/** + * Create an async function that only executes once per instance of an object. + * Once called on a given object, the same promise will be returned for any + * future calls for that object. + * + * @param Function func + * The generator function that to wrap as an async function. + * @return Function + * The async function. + */ +exports.asyncOnce = function asyncOnce(func) { + const promises = new WeakMap(); + return function (...args) { + let promise = promises.get(this); + if (!promise) { + promise = Task.spawn(func.apply(this, args)); + promises.set(this, promise); + } + return promise; + }; +}; + +/** + * Adds an event listener to the given element, and then removes its event + * listener once the event is called, returning the event object as a promise. + * @param nsIDOMElement element + * The DOM element to listen on + * @param String event + * The name of the event type to listen for + * @param Boolean useCapture + * Should we initiate the capture phase? + * @return Promise + * The promise resolved with the event object when the event first + * happens + */ +exports.listenOnce = function listenOnce(element, event, useCapture) { + return new Promise(function (resolve, reject) { + let onEvent = function (ev) { + element.removeEventListener(event, onEvent, useCapture); + resolve(ev); + }; + element.addEventListener(event, onEvent, useCapture); + }); +}; + +/** + * Call a function that expects a callback as the last argument and returns a + * promise for the result. This simplifies using callback APIs from tasks and + * async functions. + * + * @param Any obj + * The |this| value to call the function on. + * @param Function func + * The callback-expecting function to call. + * @param Array args + * Additional arguments to pass to the method. + * @return Promise + * The promise for the result. If the callback is called with only one + * argument, it is used as the resolution value. If there's multiple + * arguments, an array containing the arguments is the resolution value. + * If the method throws, the promise is rejected with the thrown value. + */ +function promisify(obj, func, args) { + return new Promise(resolve => { + args.push((...results) => { + resolve(results.length > 1 ? results : results[0]); + }); + func.apply(obj, args); + }); +} + +/** + * Call a method that expects a callback as the last argument and returns a + * promise for the result. + * + * @see promisify + */ +exports.promiseInvoke = function promiseInvoke(obj, func, ...args) { + return promisify(obj, func, args); +}; + +/** + * Call a function that expects a callback as the last argument. + * + * @see promisify + */ +exports.promiseCall = function promiseCall(func, ...args) { + return promisify(undefined, func, args); +}; |