async-utils.js 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  1. /* This Source Code Form is subject to the terms of the Mozilla Public
  2. * License, v. 2.0. If a copy of the MPL was not distributed with this
  3. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  4. "use strict";
  5. /**
  6. * Helpers for async functions. Async functions are generator functions that are
  7. * run by Tasks. An async function returns a Promise for the resolution of the
  8. * function. When the function returns, the promise is resolved with the
  9. * returned value. If it throws the promise rejects with the thrown error.
  10. *
  11. * See Task documentation at https://developer.mozilla.org/en-US/docs/Mozilla/JavaScript_code_modules/Task.jsm.
  12. */
  13. var {Task} = require("devtools/shared/task");
  14. var Promise = require("promise");
  15. /**
  16. * Create an async function that only executes once per instance of an object.
  17. * Once called on a given object, the same promise will be returned for any
  18. * future calls for that object.
  19. *
  20. * @param Function func
  21. * The generator function that to wrap as an async function.
  22. * @return Function
  23. * The async function.
  24. */
  25. exports.asyncOnce = function asyncOnce(func) {
  26. const promises = new WeakMap();
  27. return function (...args) {
  28. let promise = promises.get(this);
  29. if (!promise) {
  30. promise = Task.spawn(func.apply(this, args));
  31. promises.set(this, promise);
  32. }
  33. return promise;
  34. };
  35. };
  36. /**
  37. * Adds an event listener to the given element, and then removes its event
  38. * listener once the event is called, returning the event object as a promise.
  39. * @param nsIDOMElement element
  40. * The DOM element to listen on
  41. * @param String event
  42. * The name of the event type to listen for
  43. * @param Boolean useCapture
  44. * Should we initiate the capture phase?
  45. * @return Promise
  46. * The promise resolved with the event object when the event first
  47. * happens
  48. */
  49. exports.listenOnce = function listenOnce(element, event, useCapture) {
  50. return new Promise(function (resolve, reject) {
  51. let onEvent = function (ev) {
  52. element.removeEventListener(event, onEvent, useCapture);
  53. resolve(ev);
  54. };
  55. element.addEventListener(event, onEvent, useCapture);
  56. });
  57. };
  58. /**
  59. * Call a function that expects a callback as the last argument and returns a
  60. * promise for the result. This simplifies using callback APIs from tasks and
  61. * async functions.
  62. *
  63. * @param Any obj
  64. * The |this| value to call the function on.
  65. * @param Function func
  66. * The callback-expecting function to call.
  67. * @param Array args
  68. * Additional arguments to pass to the method.
  69. * @return Promise
  70. * The promise for the result. If the callback is called with only one
  71. * argument, it is used as the resolution value. If there's multiple
  72. * arguments, an array containing the arguments is the resolution value.
  73. * If the method throws, the promise is rejected with the thrown value.
  74. */
  75. function promisify(obj, func, args) {
  76. return new Promise(resolve => {
  77. args.push((...results) => {
  78. resolve(results.length > 1 ? results : results[0]);
  79. });
  80. func.apply(obj, args);
  81. });
  82. }
  83. /**
  84. * Call a method that expects a callback as the last argument and returns a
  85. * promise for the result.
  86. *
  87. * @see promisify
  88. */
  89. exports.promiseInvoke = function promiseInvoke(obj, func, ...args) {
  90. return promisify(obj, func, args);
  91. };
  92. /**
  93. * Call a function that expects a callback as the last argument.
  94. *
  95. * @see promisify
  96. */
  97. exports.promiseCall = function promiseCall(func, ...args) {
  98. return promisify(undefined, func, args);
  99. };