Bonjour,

Je développe actuellement une application e-commerce dont le front est fait en TypeScript / Angular, mais je suppose que la question s'applique aussi généralement au JavaScript.

La recherche de produits est basée sur un moteur de recherche externe au backend auquel j'envoie les mots-clés et me retourne la liste des produits correspondants avec leur id. Ensuite, je fais un appel AJAX vers le backend avec les ids de ces produits et qui me retourne différentes informations sur ceux-ci, notamment le prix, le plus important.

Je ne veux pas surcharger le serveur avec un trop grand nombre de requêtes, j'envoie donc une liste d'ids en une seule fois. J'utilise une classe service qui se charge de retourner à un composant produit son prix une fois qu'il a été récupéré, à l'aide d'un observable, fonctionnement classique. Sauf que le service, quand on lui demande un prix, ne va pas chercher un seul prix mais celui de l'ensemble des produits qui lui ont été demandés à un certain moment, donc je ne peux pas utiliser un système classique de demande de prix qui retourne un observable avec uniquement le prix du produit en question.

Donc voilà comment je procède actuellement : le produit demande son prix au service en lui envoyant son id. Le service accumule la liste des ids des demandes au même moment, puis envoie la requête AJAX et stocke le les résultats dans un objet (tableau avec liste d'objets produit produits et leurs propriétés). Chaque composant produit s'abonne au service. Une fois que le service a récupéré la liste des produits demandés, il envoie un par un les ids de ces produits à tous les composants produits abonnés. Le produit, lorsqu'il a reçu cet id, vérifie si l'id correspond au sien, et si c'est le cas, demande le prix au service qui est alors capable de le lui renvoyer directement en allant le chercher dans ses données stockées.

Pour être plus claire, le produit qui demande son prix n'attend pas une réponse lors de cet appel, il informe seulement le service qu'il doit récupérer le prix, ensuite il reçoit un push lorsque le prix est disponible (ce qui peut être le cas immédiatement lorsqu'il a été stocké).

Ca fonctionne très bien mais je ne suis pas satisfaite du fonctionnement car cela déclenche beaucoup d'évènements inutiles et j'ai peur que sur un navigateur aux ressources limitées cela ne crée des ralentissements. Si j'ai 50 produits à afficher au même moment, chaque composant produit abonné va recevoir 50 retours du service dont 49 contiennent donc une information inutile et je me demandais s'il y aurait une meilleure façon de faire.

En gros, je voudrais que les produits demandent chacun leur prix au service, que celui-ci récupère par une requête tous les prix demandés au même moment puis n'envoie l'information qu'au produit concerné. Le fonctionnement serait donc le suivant :

- L'utilisateur tappe ses mots clés et la requête est envoyée au moteur de recherche
- L'application reçoit la liste des produits correspondant et les affiche dans le template sous forme de composants
- Lorsque le composant est créé, il s'abonne au service pour pouvoir recevoir des push
- Le produit demande son prix au service
- Le service attend d'avoir la liste des produits ayant besoin d'un prix et déclenche la requête
- Une fois la réponse reçue, le service envoie à chacun des produits abonnées les données le concernant et uniquement celles-ci
- Lorsque le composant est détruit, il se désabonne évidemment du service afin de ne pas accumuler les demandes et réponses

Est-il possible de faire cela ? Je dois bien avouer que ma compréhension de la librairie rxjs est assez limitée donc je ne connais pas toutes les possibilités offertes.

Voilà un résumé de mon code actuel :

Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
class ProductComponent()
{
	public product: Product; // données passée par le template
	private productServiceSubscription; // abonnement aux push du service
 
	constructor(public productsService: ProductService) { } // service passé par injection de dépendance, instancié une seule fois
 
	public componentInit() // appelé lorsque le composant est affiché dans le template
	{
		this.productService.askForPrice(this.product.id);
 
		this.productServiceSubscription.getSubscription().subscribe((productId: number) => {
			if (productId === this.product.id) {
				this.product.price = this.productService.getPriceById(this.product.id);
			}
		});
	} 
 
	public componentDestroy()
	{
		this.productServiceSubscription.unsubscribe();
	}
}
 
class ProductsService()
{
	private products: Product[] = [];
 
	private requestTimeout;
	private subscription: Subject<productId: number> = new Subject();
	private productsQueue: number[] = []; // liste des produits en attente d'un prix
 
	public getSubscription(): Subject<productId>
	{
		return this.subscription;
	}
 
	public askForPrice(productId: number): void
	{
		// Le produt a déjà été stocké, on le retourne immédiatement
		if (this.getProductById(productId)) {
			this.subscription.next(productId);
			return;
		}
 
		// Le produt n'est pas stocké, on se prépare à le demander à l'api
 
		clearTimeout(this.requestTimeout); // système de timeout permettant d'attendre d'avoir reçu une liste de produits avant de déclencher la requête
 
		this.productsQueue.push(producId);
 
		this.requestTimeout = setTimeout(() => {
			this.getPricesFromApi();
		}, 200);
	}
 
	private getPricesFromApi(): void
	{
		const client = new HttpClient();
 
		client.get('url-de-l\'api', this.productsQueue).subscribe(products: Product[] => {
			for (const product in products) {
				if (!this.getProductById(product.id)) { // on évite de stocker un même produit plusieurs fois
					this.products.push(product);
				}
 
				this.subscription.next(product.id);
			}
		});
 
		this.productsQueue = [];
	}
 
	public getPriceById(productId: number): number
	{
		return this.getProductById().price;
	}
 
	// Retourne un produit s'il est stocké dans la variable products locale
	private getProductById(productId: number): Product|null
	{
		// ....
	}
}