fetch.ts 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  1. /**
  2. * @license
  3. * Copyright Google Inc. All Rights Reserved.
  4. *
  5. * Use of this source code is governed by an MIT-style license that can be
  6. * found in the LICENSE file at https://angular.io/license
  7. */
  8. /**
  9. * @fileoverview
  10. * @suppress {missingRequire}
  11. */
  12. Zone.__load_patch('fetch', (global: any, Zone: ZoneType, api: _ZonePrivate) => {
  13. interface FetchTaskData extends TaskData {
  14. fetchArgs?: any[];
  15. }
  16. let fetch = global['fetch'];
  17. if (typeof fetch !== 'function') {
  18. return;
  19. }
  20. const originalFetch = global[api.symbol('fetch')];
  21. if (originalFetch) {
  22. // restore unpatched fetch first
  23. fetch = originalFetch;
  24. }
  25. const ZoneAwarePromise = global.Promise;
  26. const symbolThenPatched = api.symbol('thenPatched');
  27. const fetchTaskScheduling = api.symbol('fetchTaskScheduling');
  28. const fetchTaskAborting = api.symbol('fetchTaskAborting');
  29. const OriginalAbortController = global['AbortController'];
  30. const supportAbort = typeof OriginalAbortController === 'function';
  31. let abortNative: Function|null = null;
  32. if (supportAbort) {
  33. global['AbortController'] = function() {
  34. const abortController = new OriginalAbortController();
  35. const signal = abortController.signal;
  36. signal.abortController = abortController;
  37. return abortController;
  38. };
  39. abortNative = api.patchMethod(
  40. OriginalAbortController.prototype, 'abort',
  41. (delegate: Function) => (self: any, args: any) => {
  42. if (self.task) {
  43. return self.task.zone.cancelTask(self.task);
  44. }
  45. return delegate.apply(self, args);
  46. });
  47. }
  48. const placeholder = function() {};
  49. global['fetch'] = function() {
  50. const args = Array.prototype.slice.call(arguments);
  51. const options = args.length > 1 ? args[1] : null;
  52. const signal = options && options.signal;
  53. return new Promise((res, rej) => {
  54. const task = Zone.current.scheduleMacroTask(
  55. 'fetch', placeholder, {fetchArgs: args} as FetchTaskData,
  56. () => {
  57. let fetchPromise;
  58. let zone = Zone.current;
  59. try {
  60. (zone as any)[fetchTaskScheduling] = true;
  61. fetchPromise = fetch.apply(this, args);
  62. } catch (error) {
  63. rej(error);
  64. return;
  65. } finally {
  66. (zone as any)[fetchTaskScheduling] = false;
  67. }
  68. if (!(fetchPromise instanceof ZoneAwarePromise)) {
  69. let ctor = fetchPromise.constructor;
  70. if (!ctor[symbolThenPatched]) {
  71. api.patchThen(ctor);
  72. }
  73. }
  74. fetchPromise.then(
  75. (resource: any) => {
  76. if (task.state !== 'notScheduled') {
  77. task.invoke();
  78. }
  79. res(resource);
  80. },
  81. (error: any) => {
  82. if (task.state !== 'notScheduled') {
  83. task.invoke();
  84. }
  85. rej(error);
  86. });
  87. },
  88. () => {
  89. if (!supportAbort) {
  90. rej('No AbortController supported, can not cancel fetch');
  91. return;
  92. }
  93. if (signal && signal.abortController && !signal.aborted &&
  94. typeof signal.abortController.abort === 'function' && abortNative) {
  95. try {
  96. (Zone.current as any)[fetchTaskAborting] = true;
  97. abortNative.call(signal.abortController);
  98. } finally {
  99. (Zone.current as any)[fetchTaskAborting] = false;
  100. }
  101. } else {
  102. rej('cancel fetch need a AbortController.signal');
  103. }
  104. });
  105. if (signal && signal.abortController) {
  106. signal.abortController.task = task;
  107. }
  108. });
  109. };
  110. });