mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Implement caching in __loadBundleAsync (#36809)
Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/36809 Changelog: [General][Added] [2/n] Support lazy bundling in development Implements caching in `__loadBundleAsync` in line with https://github.com/react-native-community/discussions-and-proposals/pull/628. Reviewed By: jacdebug Differential Revision: D44630077 fbshipit-source-id: f1a19217f54493e5cf0c8bba910a6a6e14c10c03
This commit is contained in:
committed by
Facebook GitHub Bot
parent
799b0f4be8
commit
b66ab690fe
+43
@@ -170,3 +170,46 @@ test('shows and hides the loading view around concurrent requests', async () =>
|
||||
await promise2;
|
||||
expect(loadingViewMock.hide).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
test('loadBundleFromServer does not cache errors', async () => {
|
||||
mockHeaders = {'Content-Type': 'application/json'};
|
||||
mockDataResponse = JSON.stringify({message: 'Error thrown from Metro'});
|
||||
|
||||
await expect(
|
||||
loadBundleFromServer('/Fail.bundle?platform=ios'),
|
||||
).rejects.toThrow();
|
||||
|
||||
mockDataResponse = '"code";';
|
||||
mockHeaders = {'Content-Type': 'application/javascript'};
|
||||
|
||||
await expect(
|
||||
loadBundleFromServer('/Fail.bundle?platform=ios'),
|
||||
).resolves.not.toThrow();
|
||||
});
|
||||
|
||||
test('loadBundleFromServer caches successful fetches', async () => {
|
||||
mockDataResponse = '"code";';
|
||||
mockHeaders = {'Content-Type': 'application/javascript'};
|
||||
|
||||
const promise1 = loadBundleFromServer(
|
||||
'/Banana.bundle?platform=ios&dev=true&minify=false&unusedExtraParam=42&modulesOnly=true&runModule=false',
|
||||
);
|
||||
|
||||
// Request again in the same tick = same promise
|
||||
const promise2 = loadBundleFromServer(
|
||||
'/Banana.bundle?platform=ios&dev=true&minify=false&unusedExtraParam=42&modulesOnly=true&runModule=false',
|
||||
);
|
||||
expect(promise2).toBe(promise1);
|
||||
|
||||
await promise1;
|
||||
|
||||
// Request again once resolved = still the same promise
|
||||
const promise3 = loadBundleFromServer(
|
||||
'/Banana.bundle?platform=ios&dev=true&minify=false&unusedExtraParam=42&modulesOnly=true&runModule=false',
|
||||
);
|
||||
expect(promise3).toBe(promise1);
|
||||
|
||||
await promise2;
|
||||
|
||||
expect(sendRequest).toBeCalledTimes(1);
|
||||
});
|
||||
|
||||
@@ -18,6 +18,8 @@ declare var global: {globalEvalWithSourceUrl?: (string, string) => mixed, ...};
|
||||
|
||||
let pendingRequests = 0;
|
||||
|
||||
const cachedPromisesByUrl = new Map<string, Promise<void>>();
|
||||
|
||||
function asyncRequest(
|
||||
url: string,
|
||||
): Promise<{body: string, headers: {[string]: string}}> {
|
||||
@@ -90,9 +92,15 @@ function buildUrlForBundle(bundlePathAndQuery: string) {
|
||||
module.exports = function (bundlePathAndQuery: string): Promise<void> {
|
||||
const requestUrl = buildUrlForBundle(bundlePathAndQuery);
|
||||
|
||||
let loadPromise = cachedPromisesByUrl.get(requestUrl);
|
||||
|
||||
if (loadPromise) {
|
||||
return loadPromise;
|
||||
}
|
||||
LoadingView.showMessage('Downloading...', 'load');
|
||||
++pendingRequests;
|
||||
return asyncRequest(requestUrl)
|
||||
|
||||
loadPromise = asyncRequest(requestUrl)
|
||||
.then<void>(({body, headers}) => {
|
||||
if (
|
||||
headers['Content-Type'] != null &&
|
||||
@@ -116,9 +124,16 @@ module.exports = function (bundlePathAndQuery: string): Promise<void> {
|
||||
eval(body);
|
||||
}
|
||||
})
|
||||
.catch<void>(e => {
|
||||
cachedPromisesByUrl.delete(requestUrl);
|
||||
throw e;
|
||||
})
|
||||
.finally(() => {
|
||||
if (!--pendingRequests) {
|
||||
LoadingView.hide();
|
||||
}
|
||||
});
|
||||
|
||||
cachedPromisesByUrl.set(requestUrl, loadPromise);
|
||||
return loadPromise;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user