123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475 |
- // @flow
-
- import { timeoutPromise } from './timeoutPromise';
-
- /**
- * Loads a script from a specific URL. React Native cannot load a JS
- * file/resource/URL via a <script> HTML element, so the implementation
- * fetches the specified {@code url} as plain text using {@link fetch()} and
- * then evaluates the fetched string as JavaScript code (using {@link eval()}).
- *
- * @param {string} url - The absolute URL from which the script is to be
- * (down)loaded.
- * @param {number} [timeout] - The timeout in millisecnods after which the
- * loading of the specified {@code url} is to be aborted/rejected (if not
- * settled yet).
- * @returns {void}
- */
- export function loadScript(url: string, timeout: ?number): Promise<void> {
- return new Promise((resolve, reject) => {
- // XXX The implementation of fetch on Android will throw an Exception on
- // the Java side which will break the app if the URL is invalid (which
- // the implementation of fetch on Android calls 'unexpected url'). In
- // order to try to prevent the breakage of the app, try to fail on an
- // invalid URL as soon as possible.
- const { hostname, pathname, protocol } = new URL(url);
-
- // XXX The standard URL implementation should throw an Error if the
- // specified URL is relative. Unfortunately, the polyfill used on
- // react-native does not.
- if (!hostname || !pathname || !protocol) {
- reject(`unexpected url: ${url}`);
-
- return;
- }
-
- let fetch_ = fetch(url, { method: 'GET' });
-
- // The implementation of fetch provided by react-native is based on
- // XMLHttpRequest. Which defines timeout as an unsigned long with
- // default value 0, which means there is no timeout.
- if (timeout) {
- // FIXME I don't like the approach with timeoutPromise because:
- //
- // * It merely abandons the underlying XHR and, consequently, opens
- // us to potential issues with NetworkActivityIndicator which
- // tracks XHRs.
- //
- // * @paweldomas also reported that timeouts seem to be respected by
- // the XHR implementation on iOS. Given that we have
- // implementation of loadScript based on fetch and XHR (in an
- // earlier revision), I don't see why we're not using an XHR
- // directly on iOS.
- //
- // * The approach of timeoutPromise I found on the Internet is to
- // directly use XHR instead of fetch and abort the XHR on timeout.
- // Which may deal with the NetworkActivityIndicator at least.
- fetch_ = timeoutPromise(fetch_, timeout);
- }
-
- fetch_
- .then(response => {
- switch (response.status) {
- case 200:
- return response.responseText || response.text();
-
- default:
- throw response.statusText;
- }
- })
- .then(responseText => {
- eval.call(window, responseText); // eslint-disable-line no-eval
- })
- .then(resolve, reject);
- });
- }
|