fetchWithRetries.js 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. /**
  2. * Copyright (c) 2013-present, Facebook, Inc.
  3. *
  4. * This source code is licensed under the MIT license found in the
  5. * LICENSE file in the root directory of this source tree.
  6. *
  7. * @typechecks
  8. *
  9. */
  10. 'use strict';
  11. var Promise = require('./Promise');
  12. function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }
  13. var ExecutionEnvironment = require('./ExecutionEnvironment');
  14. var sprintf = require('./sprintf');
  15. var fetch = require('./fetch');
  16. var warning = require('./warning');
  17. var DEFAULT_TIMEOUT = 15000;
  18. var DEFAULT_RETRIES = [1000, 3000];
  19. /**
  20. * Makes a POST request to the server with the given data as the payload.
  21. * Automatic retries are done based on the values in `retryDelays`.
  22. */
  23. function fetchWithRetries(uri, initWithRetries) {
  24. var _ref = initWithRetries || {},
  25. fetchTimeout = _ref.fetchTimeout,
  26. retryDelays = _ref.retryDelays,
  27. init = _objectWithoutProperties(_ref, ['fetchTimeout', 'retryDelays']);
  28. var _fetchTimeout = fetchTimeout != null ? fetchTimeout : DEFAULT_TIMEOUT;
  29. var _retryDelays = retryDelays != null ? retryDelays : DEFAULT_RETRIES;
  30. var requestsAttempted = 0;
  31. var requestStartTime = 0;
  32. return new Promise(function (resolve, reject) {
  33. /**
  34. * Sends a request to the server that will timeout after `fetchTimeout`.
  35. * If the request fails or times out a new request might be scheduled.
  36. */
  37. function sendTimedRequest() {
  38. requestsAttempted++;
  39. requestStartTime = Date.now();
  40. var isRequestAlive = true;
  41. var request = fetch(uri, init);
  42. var requestTimeout = setTimeout(function () {
  43. isRequestAlive = false;
  44. if (shouldRetry(requestsAttempted)) {
  45. process.env.NODE_ENV !== 'production' ? warning(false, 'fetchWithRetries: HTTP timeout, retrying.') : void 0;
  46. retryRequest();
  47. } else {
  48. reject(new Error(sprintf('fetchWithRetries(): Failed to get response from server, ' + 'tried %s times.', requestsAttempted)));
  49. }
  50. }, _fetchTimeout);
  51. request.then(function (response) {
  52. clearTimeout(requestTimeout);
  53. if (isRequestAlive) {
  54. // We got a response, we can clear the timeout.
  55. if (response.status >= 200 && response.status < 300) {
  56. // Got a response code that indicates success, resolve the promise.
  57. resolve(response);
  58. } else if (shouldRetry(requestsAttempted)) {
  59. // Fetch was not successful, retrying.
  60. // TODO(#7595849): Only retry on transient HTTP errors.
  61. process.env.NODE_ENV !== 'production' ? warning(false, 'fetchWithRetries: HTTP error, retrying.') : void 0, retryRequest();
  62. } else {
  63. // Request was not successful, giving up.
  64. var error = new Error(sprintf('fetchWithRetries(): Still no successful response after ' + '%s retries, giving up.', requestsAttempted));
  65. error.response = response;
  66. reject(error);
  67. }
  68. }
  69. })['catch'](function (error) {
  70. clearTimeout(requestTimeout);
  71. if (shouldRetry(requestsAttempted)) {
  72. retryRequest();
  73. } else {
  74. reject(error);
  75. }
  76. });
  77. }
  78. /**
  79. * Schedules another run of sendTimedRequest based on how much time has
  80. * passed between the time the last request was sent and now.
  81. */
  82. function retryRequest() {
  83. var retryDelay = _retryDelays[requestsAttempted - 1];
  84. var retryStartTime = requestStartTime + retryDelay;
  85. // Schedule retry for a configured duration after last request started.
  86. setTimeout(sendTimedRequest, retryStartTime - Date.now());
  87. }
  88. /**
  89. * Checks if another attempt should be done to send a request to the server.
  90. */
  91. function shouldRetry(attempt) {
  92. return ExecutionEnvironment.canUseDOM && attempt <= _retryDelays.length;
  93. }
  94. sendTimedRequest();
  95. });
  96. }
  97. module.exports = fetchWithRetries;