mapbox / mapbox/mapbox-gl-js

asynchronous `addImage(...)` design options

Open
#9,018 9 comments 7 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

api :memo: feature :green_apple:
Dominant language
TypeScript
Stars
12.4k
Forks
2.4k
PR merge metrics
No merged PRs in 30d

Description

## Motivation

Images can be generated or loaded on demand by listening to the `styleimagemissing` event and then providing the image with `addImage(...)`. The problem is that you need to call `addImage(...)` immediately and it only accepts images that are complete. You have no time to load any new assets!

We need some way of providing images that may not be loaded yet so that we can block tile parsing until that happens.

## Design Alternatives

### Option A: Placeholders

Placeholders block all tiles that use the image. They need to be replaced with the real image or removed. The main disadvantage is that it may be easier to forget to handle the error case.

```js
// add placeholder for image
map.addImage('name', { complete: false });
...
if (error) {
// remove image in case of failure
map.removeImage('name');
} else {
// add loaded image
map.updateImage('name', realImage });
}
```

### Option B: old-fashioned event handlers

Placeholders block all tiles that use the image. They need to be replaced with the real image or removed.

```js

const image = {};
map.addImage('name', image);
...
if (error) {
// remove an image in case of failure
image.onerror();
} else {
// add loaded image
image.width = 10;
image.height = 10;
image.data = ...;
image.onload();
}
```

### Option C: Promise-like

This would accept either real promises or promise-like objects that implement the `then(...)` part of the promise interface.

```js
// With IE support:
let resolve, reject;
map.addImage({ then: function(res, rej) { resolve = res; reject = rej; });

// Without IE support:
map.addImage(new Promise((resolve, reject) => { ... });

...
if (error) {
reject(error);
} else {
resolve(myLoadedImage);
}
```

Alternative: we could also accept the same argument used to construct promises.
```js
map.addImage((resolve, reject) => { ... });
```

### Option D: Callbacks

I'm not sure this follows any existing precedent but it's pretty simple?

```js
const callback = map.addImageAsync('name');
...
if (error) {
callback(error);
} else {
callback(null, myLoadedImage);
}
```

## Design

I'm actually not sure which alternative I prefer. Some have a smaller api footprint. Some would extend better to other potentially async values. Some make error handling more explicit. Some are more familiar.

What do you think?

### Implementation

ImageManager would store placeholder values for all images being loaded. When a worker requests an image that is not yet ready it would get added to the list of pending requests. Each time an image is loaded it would check whether any requests can be fullfilled.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reading ImageManager and the addImage API, then trace how styleimagemissing requests reach workers and how pending image requests are handled. Compare the placeholder, event-handler, promise-like, and callback options before choosing an API. Done means one design is selected and the pending requests are fulfilled when an image loads or fails.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript, typescript
Domain
frontend, web-dev
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
20/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.