From f6f34f82e598b9d7c5c570838962c3fcd9947c73 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Sat, 20 Nov 2021 13:49:10 -0700 Subject: [PATCH] Allow data tile source loader to return a value or a promise --- examples/data-tiles.js | 2 +- src/ol/functions.js | 21 +++++++ src/ol/source/DataTile.js | 20 +++++-- .../spec/ol/renderer/webgl/Layer.test.js | 2 +- .../spec/ol/renderer/webgl/TileLayer.test.js | 2 +- test/node/ol/functions.test.js | 58 ++++++++++++++++++- .../cases/webgl-data-tile-3-band/main.js | 2 +- .../webgl-data-tile-loosely-packed/main.js | 2 +- .../cases/webgl-mixed-layers/main.js | 2 +- .../cases/webgl-multiple-layers/main.js | 2 +- 10 files changed, 100 insertions(+), 13 deletions(-) diff --git a/examples/data-tiles.js b/examples/data-tiles.js index 27390a8ab9..5ebc97ade8 100644 --- a/examples/data-tiles.js +++ b/examples/data-tiles.js @@ -32,7 +32,7 @@ const map = new Map({ context.strokeRect(0, 0, size, size); const data = context.getImageData(0, 0, size, size).data; // converting to Uint8Array for increased browser compatibility - return Promise.resolve(new Uint8Array(data.buffer)); + return new Uint8Array(data.buffer); }, // disable opacity transition to avoid overlapping labels during tile loading transition: 0, diff --git a/src/ol/functions.js b/src/ol/functions.js index 87fa024d5e..da92686985 100644 --- a/src/ol/functions.js +++ b/src/ol/functions.js @@ -58,3 +58,24 @@ export function memoizeOne(fn) { return lastResult; }; } + +/** + * @template T + * @param {function(): (T | Promise)} getter A function that returns a value or a promise for a value. + * @return {Promise} A promise for the value. + */ +export function toPromise(getter) { + function promiseGetter() { + let value; + try { + value = getter(); + } catch (err) { + return Promise.reject(err); + } + if (value instanceof Promise) { + return value; + } + return Promise.resolve(value); + } + return promiseGetter(); +} diff --git a/src/ol/source/DataTile.js b/src/ol/source/DataTile.js index 7f8e570b3c..903d3c0b35 100644 --- a/src/ol/source/DataTile.js +++ b/src/ol/source/DataTile.js @@ -10,11 +10,18 @@ import {assign} from '../obj.js'; import {createXYZ, extentFromProjection} from '../tilegrid.js'; import {getKeyZXY} from '../tilecoord.js'; import {getUid} from '../util.js'; +import {toPromise} from '../functions.js'; + +/** + * Data tile loading function. The function is called with z, x, and y tile coordinates and + * returns {@link import("../DataTile.js").Data data} for a tile or a promise for the same. + * @typedef {function(number, number, number) : (import("../DataTile.js").Data|Promise)} Loader + */ /** * @typedef {Object} Options - * @property {function(number, number, number) : Promise} [loader] Data loader. Called with z, x, and y tile coordinates. - * Returns a promise that resolves to a {@link import("../DataTile.js").Data}. + * @property {Loader} [loader] Data loader. Called with z, x, and y tile coordinates. + * Returns {@link import("../DataTile.js").Data data} for a tile or a promise for the same. * @property {number} [maxZoom=42] Optional max zoom level. Not used if `tileGrid` is provided. * @property {number} [minZoom=0] Optional min zoom level. Not used if `tileGrid` is provided. * @property {number|import("../size.js").Size} [tileSize=[256, 256]] The pixel width and height of the tiles. @@ -31,7 +38,7 @@ import {getUid} from '../util.js'; /** * @classdesc - * Base class for sources providing tiles divided into a tile grid. + * A source for typed array data tiles. * * @fires import("./Tile.js").TileSourceEvent * @api @@ -86,7 +93,7 @@ class DataTileSource extends TileSource { } /** - * @param {function(number, number, number) : Promise} loader The data loader. + * @param {Loader} loader The data loader. * @protected */ setLoader(loader) { @@ -109,8 +116,11 @@ class DataTileSource extends TileSource { } const sourceLoader = this.loader_; + function loader() { - return sourceLoader(z, x, y); + return toPromise(function () { + return sourceLoader(z, x, y); + }); } const tile = new DataTile( diff --git a/test/browser/spec/ol/renderer/webgl/Layer.test.js b/test/browser/spec/ol/renderer/webgl/Layer.test.js index 4189c08182..1d76de8a36 100644 --- a/test/browser/spec/ol/renderer/webgl/Layer.test.js +++ b/test/browser/spec/ol/renderer/webgl/Layer.test.js @@ -252,7 +252,7 @@ describe('ol/renderer/webgl/Layer', function () { className: className, source: new DataTileSource({ loader(z, x, y) { - return Promise.resolve(new ImageData(256, 256)); + return new ImageData(256, 256); }, }), }); diff --git a/test/browser/spec/ol/renderer/webgl/TileLayer.test.js b/test/browser/spec/ol/renderer/webgl/TileLayer.test.js index 0c7d4bf763..65650a1c4b 100644 --- a/test/browser/spec/ol/renderer/webgl/TileLayer.test.js +++ b/test/browser/spec/ol/renderer/webgl/TileLayer.test.js @@ -25,7 +25,7 @@ describe('ol/renderer/webgl/TileLayer', function () { context.fillStyle = 'rgba(100, 100, 100, 0.5)'; context.fillRect(0, 0, size, size); const data = context.getImageData(0, 0, size, size).data; - return Promise.resolve(data); + return data; }, }), }); diff --git a/test/node/ol/functions.test.js b/test/node/ol/functions.test.js index f3001a3533..813f748a05 100644 --- a/test/node/ol/functions.test.js +++ b/test/node/ol/functions.test.js @@ -1,7 +1,63 @@ import expect from '../expect.js'; -import {memoizeOne} from '../../../src/ol/functions.js'; +import {memoizeOne, toPromise} from '../../../src/ol/functions.js'; describe('ol/functions.js', function () { + describe('toPromise()', () => { + it('returns a promise given a getter for a value', (done) => { + const getter = () => 'a value'; + const promise = toPromise(getter); + expect(promise).to.be.a(Promise); + promise.then((value) => { + expect(value).to.be('a value'); + done(); + }, done); + }); + + it('returns a promise given a getter for a promise that resolves', (done) => { + const getter = () => Promise.resolve('a value'); + const promise = toPromise(getter); + expect(promise).to.be.a(Promise); + promise.then((value) => { + expect(value).to.be('a value'); + done(); + }, done); + }); + + it('returns a promise that rejects given a getter that throws', (done) => { + const getter = () => { + throw new Error('an error'); + }; + const promise = toPromise(getter); + expect(promise).to.be.a(Promise); + promise.then( + (value) => { + done(new Error(`expected promise to reject, got ${value}`)); + }, + (err) => { + expect(err).to.be.an(Error); + expect(err.message).to.be('an error'); + done(); + } + ); + }); + + it('returns a promise that rejects given a getter for a promse that rejects', (done) => { + const getter = () => Promise.reject(new Error('an error')); + const promise = toPromise(getter); + expect(promise).to.be.a(Promise); + promise.then( + (value) => { + done(new Error(`expected promise to reject, got ${value}`)); + }, + (err) => { + expect(err).to.be.an(Error); + expect(err.message).to.be('an error'); + done(); + } + ); + }); + }); + describe('memoizeOne()', function () { it('returns the result from the first call when called a second time with the same args', function () { const arg1 = {}; diff --git a/test/rendering/cases/webgl-data-tile-3-band/main.js b/test/rendering/cases/webgl-data-tile-3-band/main.js index 951710c3a1..668215a2c6 100644 --- a/test/rendering/cases/webgl-data-tile-3-band/main.js +++ b/test/rendering/cases/webgl-data-tile-3-band/main.js @@ -34,7 +34,7 @@ new Map({ const bandCount = 3; const result = data.filter((_, index) => index % 4 < bandCount); - return Promise.resolve(result); + return result; }, tileSize: size, }), diff --git a/test/rendering/cases/webgl-data-tile-loosely-packed/main.js b/test/rendering/cases/webgl-data-tile-loosely-packed/main.js index d7bf1260af..d84156808d 100644 --- a/test/rendering/cases/webgl-data-tile-loosely-packed/main.js +++ b/test/rendering/cases/webgl-data-tile-loosely-packed/main.js @@ -53,7 +53,7 @@ new Map({ } } - return Promise.resolve(output); + return output; }, }), }), diff --git a/test/rendering/cases/webgl-mixed-layers/main.js b/test/rendering/cases/webgl-mixed-layers/main.js index 1c4ba1f44d..31f4e24f19 100644 --- a/test/rendering/cases/webgl-mixed-layers/main.js +++ b/test/rendering/cases/webgl-mixed-layers/main.js @@ -66,7 +66,7 @@ new Map({ labelCanvasSize, labelCanvasSize ).data; - return Promise.resolve(new Uint8Array(data.buffer)); + return new Uint8Array(data.buffer); }, transition: 0, }), diff --git a/test/rendering/cases/webgl-multiple-layers/main.js b/test/rendering/cases/webgl-multiple-layers/main.js index 304b5805d9..d0021531e8 100644 --- a/test/rendering/cases/webgl-multiple-layers/main.js +++ b/test/rendering/cases/webgl-multiple-layers/main.js @@ -64,7 +64,7 @@ new Map({ labelCanvasSize, labelCanvasSize ).data; - return Promise.resolve(new Uint8Array(data.buffer)); + return new Uint8Array(data.buffer); }, transition: 0, }),