-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Description
Which package is this a feature request for?
Task (@lit-labs/task)
Description
The lack of an equality function to compare task arguments can cause some major DX problems.
Using an object or array literal from the args function will cause the task to continually fire, usually creating an iloop as the task completing causes another update cycle which checks for new arguments:
_task = new Task<[Array<string>], Foo>(this, async ([strings]) => { ... }, () => ['a', 'b', 'c']);To avoid this you have to hoist the array if you can, so that the === check works:
const strings = ['a', 'b', 'c'];
_task = new Task<[Array<string>], Foo>(this, async ([strings]) => { ... }, () => string);It's worse when you have an argument that's a deep object. This can be common if you're using Task to wrap an existing async API that takes such an object as part of its parameters.
Here's a controller that wraps the Google Maps Nearby Places API, which takes a PlaceSearchRequest request. We'd like to use Task to manage the API calls, so it's natural to ask for a PlaceSearchRequest from a user of the controller and pass that as a task arg.
import {ReactiveController, ReactiveControllerHost} from 'lit';
import {initialState, StatusRenderer, Task} from '@lit-labs/task';
export interface Options {
getMap: () => google.maps.Map | undefined;
getRequest: () => Partial<google.maps.places.PlaceSearchRequest> | undefined;
}
export class NearbySearchController implements ReactiveController {
protected _host: ReactiveControllerHost;
private _task: Task;
constructor(host: ReactiveControllerHost, options: Options) {
(this._host = host).addController(this);
this._task = new Task<[
google.maps.Map,
google.maps.places.PlaceSearchRequest
], google.maps.places.PlaceResult[]>(
host,
async ([map, request]) => {
if (request === undefined) {
return initialState;
}
const service = new google.maps.places.PlacesService(map);
return new Promise((res, rej) =>
service.nearbySearch(request, (results, status) => {
if (status === google.maps.places.PlacesServiceStatus.OK) {
res(results);
} else {
rej(status);
}
})
);
},
() => {
const request = options.getRequest();
return [options.getMap(), request] as [
google.maps.Map,
google.maps.places.PlaceSearchRequest
];
}
);
}
render(renderer: StatusRenderer<google.maps.places.PlaceResult>) {
return this._task.render(renderer);
}
hostUpdate() {}
}_nearbySearchController = new NearbySearchController(this, {
getMap: () => this.map,
getRequest: () => ({
keyword: 'one two'
location: this.location
}),
});This construction will cause the Task to run every update as well. The only solution is to destructure the PlaceSearchRequest object into literals and many arguments before passing to Task.
I would propose three built-in equality checks:
- Strict (default)
- Shallow
- Deep
Alternatives and Workarounds
Destructuring args into primitives, hoisting const args.
Metadata
Metadata
Assignees
Labels
Type
Projects
Status