Skip to content

Commit fae8b64

Browse files
committed
feat: remove instanceof Promise checks from geojson_source
The point of this commit is to delay the resolution of the result of `_getLoadGeoJSONParameters` until the `_isUpdatingWorker` is set; i.e., inside `_dispatchWorkerUpdate`. Simply `await`ing the result of `_getLoadGeoJSONParameters` immediately after the call of the function breaks the behavior when `setData` or `setClusterOptions` are consecutively called. It would cause a race condition.
1 parent e605ce0 commit fae8b64

File tree

2 files changed

+22
-24
lines changed

2 files changed

+22
-24
lines changed

src/source/geojson_source.test.ts

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ describe('GeoJSONSource.setData', () => {
140140
await expect(promise).resolves.toBeDefined();
141141
});
142142

143-
test('respects collectResourceTiming parameter on source', () => {
143+
test('respects collectResourceTiming parameter on source', async () => {
144144
const source = createSource({collectResourceTiming: true});
145145
source.map = {
146146
_requestManager: {
@@ -158,6 +158,7 @@ describe('GeoJSONSource.setData', () => {
158158
});
159159
};
160160
source.setData('http://localhost/nonexistent');
161+
await sleep(0); // to resolve pending `await`s
161162
expect(spy).toHaveBeenCalled();
162163
});
163164

@@ -264,7 +265,7 @@ describe('GeoJSONSource.update', () => {
264265
transform.setZoom(15);
265266
transform.setLocationAtPoint(lngLat, point);
266267

267-
test('sends initial loadData request to dispatcher', () => {
268+
test('sends initial loadData request to dispatcher', async () => {
268269
const spy = vi.fn();
269270
const mockDispatcher = wrapDispatcher({
270271
sendAsync(message: ActorMessage<MessageType>) {
@@ -275,10 +276,11 @@ describe('GeoJSONSource.update', () => {
275276
});
276277

277278
new GeoJSONSource('id', {data: {}} as GeoJSONSourceOptions, mockDispatcher, undefined).load();
279+
await sleep(); // to resolve pending `await`s
278280
expect(spy).toHaveBeenCalled();
279281
});
280282

281-
test('forwards geojson-vt options with worker request', () => {
283+
test('forwards geojson-vt options with worker request', async () => {
282284
const spy = vi.fn();
283285
const mockDispatcher = wrapDispatcher({
284286
sendAsync(message: ActorMessage<any>) {
@@ -303,10 +305,11 @@ describe('GeoJSONSource.update', () => {
303305
buffer: 16,
304306
generateId: true
305307
} as GeoJSONSourceOptions, mockDispatcher, undefined).load();
308+
await sleep(0); // to resolve pending `await`s
306309
expect(spy).toHaveBeenCalled();
307310
});
308311

309-
test('forwards Supercluster options with worker request', () => {
312+
test('forwards Supercluster options with worker request', async () => {
310313
const spy = vi.fn();
311314
const mockDispatcher = wrapDispatcher({
312315
sendAsync(message) {
@@ -333,6 +336,7 @@ describe('GeoJSONSource.update', () => {
333336
generateId: true
334337
} as GeoJSONSourceOptions, mockDispatcher, undefined);
335338
source.load();
339+
await sleep(0); // to resolve pending `await`s
336340
expect(spy).toHaveBeenCalled();
337341
});
338342

@@ -362,6 +366,7 @@ describe('GeoJSONSource.update', () => {
362366
spy.mockClear();
363367

364368
source.setClusterOptions({cluster: true, clusterRadius: 80, clusterMaxZoom: 16});
369+
await sleep(0); // to resolve pending `await`s
365370

366371
expect(spy).toHaveBeenCalledTimes(1);
367372
expect(spy.mock.calls[0][0].type).toBe(MessageType.loadData);
@@ -390,7 +395,7 @@ describe('GeoJSONSource.update', () => {
390395

391396
// Wait for initial data to be loaded
392397
source.load();
393-
await waitForEvent(source, 'data', (e: MapSourceDataEvent) => e.sourceDataType === 'metadata');
398+
await sleep(0); // to resolve all the pending `await`s
394399

395400
spy.mockClear();
396401

@@ -403,7 +408,7 @@ describe('GeoJSONSource.update', () => {
403408
source.setData(sourceData2);
404409
source.setClusterOptions({cluster: true, clusterRadius: 80, clusterMaxZoom: 16});
405410

406-
await waitForEvent(source, 'data', (e: MapSourceDataEvent) => e.sourceDataType === 'metadata');
411+
await sleep(0); // to resolve all the pending `await`s
407412

408413
expect(spy).toHaveBeenCalledTimes(2);
409414
expect(spy.mock.calls[0][0].type).toBe(MessageType.loadData);
@@ -460,7 +465,7 @@ describe('GeoJSONSource.update', () => {
460465
source.updateData(diff);
461466
source.setClusterOptions({cluster: true, clusterRadius: 80, clusterMaxZoom: 16});
462467

463-
await waitForEvent(source, 'data', (e: MapSourceDataEvent) => e.sourceDataType === 'metadata');
468+
await sleep(0); // to resolve all the pending `await`s
464469

465470
expect(spy).toHaveBeenCalledTimes(2);
466471
expect(spy.mock.calls[0][0].data.cluster).toBe(false);
@@ -470,7 +475,7 @@ describe('GeoJSONSource.update', () => {
470475
expect(spy.mock.calls[1][0].data.dataDiff).not.toBeDefined();
471476
});
472477

473-
test('forwards Supercluster options with worker request, ignore max zoom of source', () => {
478+
test('forwards Supercluster options with worker request, ignore max zoom of source', async () => {
474479
const spy = vi.fn();
475480
vi.spyOn(console, 'warn').mockImplementation(() => {});
476481
const mockDispatcher = wrapDispatcher({
@@ -499,6 +504,7 @@ describe('GeoJSONSource.update', () => {
499504
generateId: true
500505
} as GeoJSONSourceOptions, mockDispatcher, undefined);
501506
source.load();
507+
await sleep(0); // to resolve pending `await`s
502508
expect(spy).toHaveBeenCalled();
503509
});
504510

src/source/geojson_source.ts

Lines changed: 8 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -390,8 +390,8 @@ export class GeoJSONSource extends Evented implements Source {
390390
}
391391

392392
const {data, diff} = this._pendingWorkerUpdate;
393-
const paramsMaybePromise = this._getLoadGeoJSONParameters(data, diff);
394-
const params = paramsMaybePromise instanceof Promise ? await paramsMaybePromise : paramsMaybePromise;
393+
// delay awaiting params until _isUpdatingWorker is set, otherwise, a race condition can happen
394+
const params = this._getLoadGeoJSONParameters(data, diff);
395395

396396
if (data !== undefined) {
397397
this._pendingWorkerUpdate.data = undefined;
@@ -408,23 +408,14 @@ export class GeoJSONSource extends Evented implements Source {
408408
/**
409409
* Create the parameters object that will be sent to the worker and used to load GeoJSON.
410410
*/
411-
private _getLoadGeoJSONParameters(data: string | GeoJSON.GeoJSON<GeoJSON.Geometry>, diff: GeoJSONSourceDiff): LoadGeoJSONParameters | Promise<LoadGeoJSONParameters> {
411+
private async _getLoadGeoJSONParameters(data: string | GeoJSON.GeoJSON<GeoJSON.Geometry>, diff: GeoJSONSourceDiff): Promise<LoadGeoJSONParameters> {
412412
const params: LoadGeoJSONParameters = extend({type: this.type}, this.workerOptions);
413413

414414
// Data comes from a remote url
415415
if (typeof data === 'string') {
416-
const request = this.map._requestManager.transformRequest(browser.resolveURL(data as string), ResourceType.Source);
417-
if (request instanceof Promise) {
418-
return request.then((request) => {
419-
request.collectResourceTiming = this._collectResourceTiming;
420-
params.request = request;
421-
return params;
422-
});
423-
} else {
424-
request.collectResourceTiming = this._collectResourceTiming;
425-
params.request = request;
426-
return params;
427-
}
416+
params.request = await this.map._requestManager.transformRequest(browser.resolveURL(data as string), ResourceType.Source);
417+
params.request.collectResourceTiming = this._collectResourceTiming;
418+
return params;
428419
}
429420

430421
// Data is a geojson object
@@ -445,11 +436,12 @@ export class GeoJSONSource extends Evented implements Source {
445436
/**
446437
* Send the worker update data from the main thread to the worker
447438
*/
448-
private async _dispatchWorkerUpdate(options: LoadGeoJSONParameters) {
439+
private async _dispatchWorkerUpdate(optionsPromise: Promise<LoadGeoJSONParameters>) {
449440
this._isUpdatingWorker = true;
450441
this.fire(new Event('dataloading', {dataType: 'source'}));
451442

452443
try {
444+
const options = await optionsPromise;
453445
const result = await this.actor.sendAsync({type: MessageType.loadData, data: options});
454446
this._isUpdatingWorker = false;
455447

0 commit comments

Comments
 (0)