Ajax: async or not async? callbacks, promesses

Je me convertis vite :sweat_smile: : pour mon cas imbriqué les await sont effectivement plus pratiques quand je pars de ton exemple.

En plus, pour revenir aux promesses, c’est bien plus rapide puisque je ne perds plus de temps avec les setTimeout().

Edit:
Yes mon for asynchrone fonctionne, avec des await (merci) :slight_smile:

  let a=[]
  let b=[]
  let c=[]
  (async function () {
          for (var ii in mondict) {

            a= await apiGlobal.getMonApi(
              mondict[ii].monid
            ); 

            b= [
              ...b.map((x) => Object.assign({}, x)),
              ...a.data,
            ];

            c.push(
              Object.assign(
                {},
                ...Object.values(mondict[ii]).map((x, i, a) =>
                  ["monid", "moncode"].indexOf(
                    Object.keys(mondict[0])[i]
                  ) > -1
                    ? Object.assign(
                        {},
                        {
                          [Object.keys(mondict[0])[i]]: a[i],
                          a_courant: [
                            ...Object.values(a.data).map(
                              (x) => x.monautreid
                            ),
                          ],
                        }
                      )
                    : Object.assign({})
                )
              )
            );
          } //fin for

        })().then(() => {
          // enlève les doublons de b et c
          // Tri de c

          // met à jour ma variable reactive Vue branchée sur un v-model
          REACT.c = [...c]; 
          // met à jour ma variable reactive Vue branchée sur un v-model
          REACT.b= [...b]; 
          // met à jour ma variable reactive Vue branchée sur un v-show 
          //   d'un DOM table  pointant sur le v-model  REACT.b
          REACT.b_charge = true; 
        });
      }
  }

Édit 2:
Quand je vois l’utilisation de cette fonction asynchrone, il doit sans doute être possible de gérer les appels datatable.net. Mais bon comme ça marche avec setTimeOut() je le modifierai plus tard.

GetMonApi().then((datas_params) => {
        setTimeout(function () {
           $("#MaTable").DataTable({ bla  datas_params })
        }, 250);
})

deviendra sans doute

(async function () {
    return await GetMonApi()
 })().then((aaa) => {
    $("#MaTable").DataTable({ bla  aaa })
 })
1 « J'aime »

Est-ce que ces 2 écritures ci-dessous sont complètement interchangeables ?

(async function () {
   a=await PromiseGetMonApiA()
   b=await PromiseMaVariableReactiveVue

   return [a,b]
 })().then((resp) => {   
    AA=FctA(resp.a)
    FctB(AA, resp.b)
 })

Et

a=await PromiseGetMonApiA()
b=await PromiseMaVariableReactiveVue

Promise.all([a,b]).then((resp) => {
    AA=FctA(resp.a)
    FctB(AA, resp.b)
 })

Et qu’en est-il de l’assertion ci-dessous dans ces 2 cas ?

Dans le 2e cas sans fonction contenante asynchrone avant il y a bien une différence ? Ou alors cette fonction contenante ne sert que dans le cas où je veux faire plein de trucs comme mon for asynchrone mais est superflu dans les cas plus simples ?

Édit :

Et le 2e cas peut aussi s’écrire comme ça ?

a=await PromiseGetMonApiA()
b=await PromiseMaVariableReactiveVue

resp=Promise.all([a,b])

AA=FctA(resp.a)
FctB(AA, resp.b)

Mais comment se passe les relations de promesses, notamment les exceptions, pour les executions de FctA() et FctB() ?

Ma crainte est peut-être infondée, mais j’ai l’impression que dans la dernière écriture sans fonction contenante ni avant les promesses, ni après, chacun vit sa vie.

Je pense qu’il y a une mécomprehension de ce que fait await.
Quand tu utilises await sur une promesse, tu attends que la promesse se termine et tu récupère le résultat, le résultat n’est plus une promesse, c’est le même usage que quand tu utilises then, c’est juste la syntaxe qui change.
Si tu await a et b, ce que tu récupère n’est pas une promesse, ce n’est pas la peine de les gérer comme des promesses, ni en les mettant dans un Promise.all, ni dans un promise.resolve, ni dans un then.

Par exemple:

a=await PromiseGetMonApiA()
b=await PromiseMaVariableReactiveVue

resp=Promise.all([a,b])

AA=FctA(resp.a)
FctB(AA, resp.b)

a et b ne sont pas des promesses à cause du mot clé await, pas besoin donc de les gérer dans Promise.all. En revanche resp est une promesse parce que Promise.all renvoit une promesse, il faut que tu utilises await ou then, sinon t’es fonctions fctA et fctB risquent de mal fonctionner.

Note que dans ton exemple, a et b sont récupérés successivement, tu attend a, puis tu attends b, ça marche bien mais c’est peut être un peu plus lent que si tu les lançait en parallèle.

C’est vrai que c’est compliqué, un truc comme ça c’est mieux d’expliquer en live, par forum c’est plus difficile.

Heuuu :sweat_smile:

Ok ok. :slight_smile:

Oui, oui, pas de souci: là j’ai des besoins en série, l’un FctA() est absolument nécessaire d’être résolu pour la fonction suivante FctB() qui en a besoin. Et ce de façon itératif (mon for en fait).

Et donc cette forme dont tu parles ci-dessous convient mieux à mon besoin, mais il ne faut pas perdre de vue les cas d’utilisations de Promise.all().

1 « J'aime »

Tiens, le mieux c’est encore un exemple qui marche et que tu peux rejouer pour voir comment ça fonctionne.
J’ai fait 4 exemple: le premier avec then, le second avec then et Promise.all, le troisième avec await et le quatrième avec await et Promise.all. (et un bonus avec des appels à un service dans une boucle for)

J’ai essayé de coller à tes problématiques, j’espère qu’avec ça tu auras tout. :slight_smile:

async function getRandomNumber() {
	const res = await fetch(`https://svelte.dev/tutorial/random-number`);
	const text = await res.text();

	if (res.ok) {
		return parseInt(text,10);
	} else {
		throw new Error(text);
	}
}

function FctA(x) {
	return x*x;  
}

function FctB(x, y) {
	return x+y;  
}

function FctC(arr) {
  	return arr.reduce((acc, current) => acc + current, 0);
}

function exampleThen() {
  let global_a;
  let global_aa;
  let global_b;
  let global_bb;
  
  getRandomNumber()
  .then(function(local_a){
    global_a = local_a;
    return getRandomNumber();
  })
  .then(function(local_b){
    global_b = local_b;
    global_aa = FctA(global_a);
    global_bb = FctB(global_aa, global_b);

    console.log('exampleThen : ', global_a, global_b, global_aa, global_bb);
  })
  .catch(function(error){
    console.log('exampleThen error :',error);
  });
}

function exampleThenPromiseAll() {
  let global_a;
  let global_aa;
  let global_b;
  let global_bb;
  
  let promiseA = getRandomNumber();
  let promiseB = getRandomNumber();
  
  Promise.all([promiseA,promiseB])
  .then(function([local_a,local_b]){
    global_a = local_a;
    global_b = local_b;
    global_aa = FctA(global_a);
    global_bb = FctB(global_aa, global_b);
    console.log('exampleThenPromiseAll : ', global_a, global_b, global_aa, global_bb);
  })
  .catch(function(error){
    console.log('exampleThenPromiseAll error :',error);
  });
}

async function exampleAwait(){
  try {
    let a = await getRandomNumber();
    let b = await getRandomNumber();
    let aa = FctA(a);
    let bb = FctB(aa, b);

    console.log('exampleAwait : ', a, b, aa, bb);
  } catch (error) {
    console.log('exampleAwait error :',error);
  }
}

async function exampleAwaitPromiseAll(){
  try {
    let promiseA = getRandomNumber();
    let promiseB = getRandomNumber();
    let [a, b] = await Promise.all([promiseA, promiseB]);
    let aa = FctA(a);
    let bb = FctB(aa, b);

    console.log('exampleAwaitPromiseAll : ', a, b, aa, bb);
  } catch (error) {
    console.log('exampleAwaitPromiseAll error :',error);
  }
}

async function exampleAwaitPromiseAllFor(){
  try {
    let arrayPromise = []
    for (let i=0 ; i<3 ; i++){
    	arrayPromise[i] = getRandomNumber();  
    }
    
    let arrayRandom = await Promise.all(arrayPromise);
    let cc = FctC(arrayRandom);

    console.log('exampleAwaitPromiseAllFor : ', arrayRandom, cc);
  } catch (error) {
    console.log('exampleAwaitPromiseAllFor error :',error);
  }
}
  
exampleThen();
exampleThenPromiseAll();
exampleAwait();
exampleAwaitPromiseAll();
exampleAwaitPromiseAllFor();

1 « J'aime »

Merci pour tout ces scénarios :+1: . J’ai testé et regardé cela. :slight_smile:

Pourquoi exampleAwaitPromiseAllFor() fonctionne telle quelle mais pas avec une attente comme le code ci-dessous. Je pensais que c’était ce qu’une promesse était censé faire. Et là un callback fonctionnerait non ? (Mais modifié dans la suite de mon message, et ça marche autrement)

    for (let i=0 ; i<2 ; i++){
       setTimeout(function timer() {
    	     arrayPromise[i] =  getRandomNumber();  
       }, i * 3000);
    }

Après mon attente est peut-être artificielle et pas compatible avec l’asynchrone. Je voulais simuler le fait dans le cas où ça n’arrive pas tout de suite. Dans ce cas j’ai bien pour arrayRandom fulfilled mais value undefined et pour cc undefined.

Mais bon notre discussion peut durer indéfiniment :smiley: Je cherchais simplement à tordre ton exemple pour tester.

C’est bon, j’ai trouvé un « bon » wait là :

Et là ma petite modif d’essai marche.

async function getRandomNumber() {
	const res = await fetch(`https://www.randomnumberapi.com/api/v1.0/random?min=100&max=1000&count=1`, 
        {mode: 'cors'}
    );
	const text = await res.json();
    console.log(text[0])
	if (res.ok) {
		return parseInt(text[0],10);
	} else {
		throw new Error(text);
	}
}

function FctC(arr) {
  	return arr.reduce((acc, current) => acc + current, 0);
}

function mywait (Tab)  {
 return new Promise(resolve =>
    setTimeout(() => resolve(Tab), 5000)
  );
}

async function exampleAwaitPromiseAllFor(){
  try {
    let arrayPromise = []
    for (let i=0 ; i<5 ; i++){
       arrayPromise[i] =await mywait( getRandomNumber())
    }
    let arrayRandom = await Promise.all(arrayPromise);
    let cc = FctC(arrayRandom);

    console.log('exampleAwaitPromiseAllFor : ', arrayRandom, cc);

  } catch (error) {
    console.log('exampleAwaitPromiseAllFor error :',error);
  }
}
exampleAwaitPromiseAllFor()

PS: Ton api svelte doit sans doute empêcher les sollications trop fréquentes car il me fait souvent « Failed to generate random number. Please try again », et si j’essaie https://www.randomnumberapi.com il me fait des erreurs CORS, j’ai donc modifié légèrement l’appel de l’api. (Par contre ça ne fonctionne que sous firefox cette modif CORS, mais bon c’est un détail).

1 « J'aime »

Comme on l’a vu, les promesses fonctionnent dans Vue 3, et j’ai pu les mettre en œuvre après ces échanges, ceci dans mes données calculées mais après coup :slight_smile:

En revanche visiblement l’utilisation des promesses n’a pas l’air d’être possible dans Vue 3 en directe pour les données computed directement dans le template. Il faut utiliser ce contournement de lire la donnée dans le then du fetch(exemple en Vue 2 option api), ce que je fais avec pinia en Vue 3 composition api:

   MonStore.getMonApi(props.id).then((data) => {
    
        REACT.mavar= data.find(
          (todo) => {
            return todo.id == detail_computed.value.id;
          }
        );      
    });

Des commentaires ou points de vue :slight_smile: :stuck_out_tongue: ?

Mon cas d’utilisation est pour une donnée qui tarde à être disponible au démarrage : la donnée computed alimente bien un v-model mais je ne peux la réutiliser dans le setup() pour d’autre chose au démarrage: elle est vide (par contre l’autre,detail_computed, est bien disponible.

Édit:

Cette ligne en revanche fonctionne ci-dessous depuis longtemps. Peut-être que c’est grâce au paramètre props.id du composant Vue qui fait que cette donnée est filtrée au démarrage et que ça utilise la donnée state du Store et non la donnée calculée computed:

const detail_computed = computed(() => {
      return MonStore.dataStoreState.find((todo) => {
        return todo.id == props.id;
      });
    });

Mais n’empêche que m’a permit d’appréhender ces problématiques, et que visiblement on ne peut pas utiliser de données asynchrones avec les computed.

Édit 2:

Solution pas du tout asynchrone, ou en tout cas masquée par Vue:

    const madonnee = computed(() => {
      return MonStore.dataStoreState1.find((x) => {
        return (
          x.id1==(toRaw(MonStore2.dataStoreState2.find((y) => {
              return y.id2== props.id;
            })).id1)
        )
      })
    });

Les framework reactifs ne sont pas ma spécialité, mais je pense qu’à ta place je n’essaierai pas de mettre de l’asynchrone dans mes valeurs computed.

Je me trompe peut être mais selon moi, la philosophie des computed, c’est qu’elle ne dépendent que des infos qu’on a stocké dans nos variables classiques, on n’est pas sensé chercher dans les fonction computed des infos qu’on ne connait pas déjà.
Mettre de l’asynchrone dans une fonction computed, ça revient à dire qu’on a besoin chercher des infos ailleurs (vu que l’asynchrone est utilisé pour des appels reseau, base de donnée, librairie externe…), a priori ce sont des infos qu’on n’aura pas stocké dans nos variables.

A mon avis, sur cette problématique, au lieu d’utiliser un computed directement, il faut faire en sorte qu’au changement de ta donnée ID, tu lance une fonction qui va faire la recherche asynchrone, et au retour de la promesse changer une vrai variable de ton store ou de ton composant. C’est cette variable que tu dois utiliser dans ton computed au lieu d’y faire ton appel asynchrone.

1 « J'aime »

C’est quoi ta spécialité ? :slight_smile:

Oui c’est ce que j’ai fini par comprendre (et le créateur de Vue refuse les computed asynchrone pour cette raison).

Mon cas en cours, cf mon dernier message, est en fait un sous formulaire récupérant un identifiant permettant de récupérer une ligne d’une donnée amont computed1 d’un DataStore1, et affichant un <select> pointant sur computed2 d’un DataStore2.
* computed1 et computed2 sont bien remplis dans le template
* mais la jointure permettant de mettre à jour l’<option> de départ ne ramenait rien sauf à faire un nouveau computed à partir des DataStore d’origine, voir mon message précédent (ou un SetTimeOut() à l’origine) et pas à partir de computed1 et computed2. Mais ce n’est pas satisfaisant si je voulais le rendre évolutif à partir de données computed et non computed, et comprendre cela pour d’autres cas ultérieures.

Donc j’ai fait ça avec les watchers VueJs (que j’utilisais déjà par ailleurs, lien) après avoir lu ta réponse et un post Stackoverflow, mais sans promesse (snif), et avec la variable REACT=reactive() que j’utilise depuis mes débuts en Vuejs.

    let REACT = reactive({
      Data1List_charge: false,
      Data2List_charge: false,
      Data2List_options_id_courant: [
        {
         id2: 0,
        },
      ],
    });

    MonStore.getData1(props.id).then(() => {
      REACT.Data1List_charge= true
    });

    MonStore.getData2().then(() => {
      REACT.Data2List_charge= true
    });

    const computed1= computed(() => {
      return MonStore.DataStore1.find((todo) => {
        return todo.id1 == props.id_fenêtre_appelante;
      });
    });

    const computed2= computed(() => {
      return MonStore.DataStore2;
    });

    watchEffect(() => {
      if (REACT.Data1List_charge && REACT.Data2List_charge) {
        REACT.Data2List_options_id_courant= toRaw(computed2.value).find(
          (todo) => {
            return todo.id2== toRaw(computed1.value).id2;
          }
        );
      }
    })

PS: J’ai passé tout mon programme en promesses et tout fonctionne mieux et plus rapidement en plus, même les plus inattendus : (async function () { return await GetMonApi() })().then((a) => {$('#MaTable').DataTable({}) }).

CommitStrip sur le refactoring (22/06/2021) :slight_smile:

« Ça nous hante ! »

3 « J'aime »

Je fais pas mal de javascript mais sur du vieux code, alors mes connaissances sont assez théoriques sur tes problèmes, je ne fais jamais ni de Promise et encore moins de Vue au quotidien. ^^;
Si j’ai une spécialité c’est plus sur la perf, surtout sur les requêtes Oracle. ^^

Ha :slight_smile: J’ai fait pas mal de requêtes optimisées aux petits oignons sur Oracle et SQL Server pendant presque 20 ans, mais j’ai réussi à changer depuis 5 ans: j’en fais toujours mais je ne le mets plus en avant, pour faire du développement web.

1 « J'aime »

J’ai un petit souci avec des récupérations de données trop asynchrones. :slight_smile:

En fait leurs récupérations se fait dans le désordre quand on tape trop rapidement dans l’<input>.

La détection de ces événements se fait grâce aux watchers de VueJs.

let REACT = reactive({
  moninput: "",
  datasTableCible:[],
}
watchEffect(async () => {
  REACT.datasTableCible = await getByPostDatas(REACT.moninput);
})

Et donc si je tape lenteeeement dans l’input par exemple m, j’ai plein de lignes, puis mon, beaucoup moins, et montagne, zéro ligne.

C’est ce qui est attendu.

Par contre si je tape vite dans l’input, si c’est bien détecté dans le watcher dans l’ordre et je le vois par les console.log(), en revanche les récupérations de données se font dans le désordre : si je tape montagne rapidement je vais voir des données alors que je ne devrais pas, en effet je vois les post se faire dans le désordre.

Sauf quand je rajoute la fonction ci-dessous, copiée des messages plus haut, mais il faut que je mette ≥ 4 s (4000ms).

function mywait (Tab)  {
  return new Promise(resolve =>
    setTimeout(() => resolve(Tab), 4000)
);

watchEffect(async () => {
  REACT.datasTableCible = mywait( getByPostDatas(REACT.moninput));
})

3 remarques :

  • En fait je recherche un déroulement en série et pas en parallèle
  • Je viens de voir dans le 2e lien qu’on peut faire un return() dans un watcher, et donc je suppose potentiellement brancher mon REACT.datasTableCible dessus. Mais je doute que le return() de l’observateur d’événements me serve, en pur ES6+ ou la surcouche VueJs, puisque je ne vois pas comment la récupération se ferait en série alors qu’il est déjà dans le désordre avant la fin du watcher. Cet article semble très riche, il faut peut-être que je l’étudie davantage.
  • Pour le coup un callback serait en série ?

PS: Ce qui est surprenant c’est que si mon souci est en ES6+, visiblement le composant jquery datatable.net n’a pas ce souci. Mais là il s’agit d’une <table> « normale » dans un <template> VueJs normale.

Une solution serait aussi d’utiliser un bouton pour envoyer les données mais c’est moins souple pour les utilisateurs. Et de toute façon ça me titille cette énigme :slight_smile:

Le setTimeout() avec 4 secondes ne paraît pas pénalisant, en effet c’est à peine plus le temps qu’on met pour taper un mot, c’est donc presque transparent pour l’utilisateur. Mais j’aimerais trouver une solution en série.

Tu n’as pas de limite du nombre de résultats retournés côté serveur?
Ça peut expliquer que tu reçoives tes résultats dans le désordre : ta première requête est plus lourde, elle met plus de temps à répondre, et le résultat arrive après celui de la seconde.

Côte js, tu as plusieurs possibilités pour éviter ce genre de problème. La plus simple, c’est de ne pas envoyer de requête tant que l’utilisateur est en train de taper : au déclenchement de l’événement, tu mets un petit timer, après lequel tu envoies ta requête. Si tu reçois un événement entre temps, tu annules ton premier timer, et tu recommences. Tu n’enverras une requête qu’une fois que ton utilisateur n’aura rien tapé pendant le temps de ton timeout.

Mmmh quitte à utiliser un timeout autant utiliser l’implémentation en promesse du setTimeout() des messages précédents, car « n’est plus en train de taper » ça risque d’être plus complexe que ça en a l’air avec les watchers. :slight_smile:

Ou alors des threads, mais en js ça n’existe pas à part les workers si j’ai bien compris.

Effectivement tu as entièrement raison, c’est logique en asynchrone, je n’avais pas fait gaffe: l’ordre de restitution est inversement proportionnelle au volume :smiley: … sauf si on attend.

Ta remarque, et un peu ta précédente, me fait penser aux piles pour avoir la sérialité. :+1: :slight_smile:

Le 2e lien a l’air sympa et simple. Il faut simplement que je vide bien la pile une fois utilisée. Avec des volumes important il ne faudrait pas que je fasse un stack overflow littéralement.

Je vais regarder ça :slight_smile:

Édit :

En fait, en raison de l’environnent asynchrone, le Array.push() met aussi les données dans l’ordre dans lequel elles arrivent, c’est-à-dire de façon désordonnée, statistiquement dans l’ordre inverse à cause de leur volume comme on l’a vu.

Mais j’ai trouvé la solution pour alimenter mes données cibles dans l’ordre dans lequel les appels de données ont été faits:

  • en incrémentant un compteur REACT.datasTableCible_PILE_NB += 1;
  • en retenant avant le push() un compteur nb = REACT.datasTableCible_PILE_NB
  • en insérant le compteur { idx: nb } au moment du push() dans ma pile REACT.datasTableCible_PILE_NB
  • en filtrant REACT.datasTableCible_PILE par le max d’idx pour le restituer dans le tableau REACT.datasTableCible sur laquelle les données de la <table> VueJs pointe.

let REACT = reactive({
  moninput: "",
  datasTableCible_charge: false,
  datasTableCible: [],
  datasTableCible_PILE: [],
  datasTableCible_PILE_NB: 0,
});
watchEffect(async () => {
  let moninput = REACT.moninput || "";

  // masque la table
  REACT.datasTableCible_charge = false;

  if (!(moninput == "")) {
    REACT.datasTableCible_PILE_NB += 1;

    // je garde en réserve le compteur
    let nb = REACT.datasTableCible_PILE_NB;
    let ret = await getByPostDatas(moninput);

    // Les données sont poussées de façon désordonnée par l'Array.push() du fait de
    // l'environnement asynchrone,
    // mais nb restera le même qu'avant l'appel des données.
    // Par exemple selon la durée le dernier et 4e appel ajax
    // va arriver en 2é dans la pile, car rapide, mais avec un idx à 4.

    REACT.datasTableCible_PILE.push([
      Object.assign({ idx: nb }, ret.length == 0? { data: []}: ret)
    ]);

    // Je recherche le max de l'idx
    let max_idx = Math.max.apply(
      Math,
      toRaw(REACT.datasTableCible_PILE).map(function (o) {
        return o[0].idx;
      })
    );

    // Je filtre REACT.datasTableCible_PILE pour idx == max_idx
    // et je le recopie dans REACT.datasTableCible
    REACT.datasTableCible = [
      ...toRaw(REACT.datasTableCible_PILE).filter(
        (x) => x[0].idx == max_idx
      )[0],
    ][0];
  } else {
    // dans le cas où l'input est vide, je purge la pile et le nombre associé
    REACT.datasTableCible_PILE.length = 0;
    REACT.datasTableCible_PILE_NB = 0;
  }
  // affiche la table
  REACT.datasTableCible_charge = true;
});

Les fonctions reactive(), watchEffect(), toRaw() sont spécifiques à VueJS, mais ma solution est a priori standard ES6+. getByPostDatas() est une fonction bidon d’exemple de récupération de données ajax via axios.

L’Array de javascript étant une pile par défaut, je n’ai rien besoin d’implémenter comme fonctionnalité spéciale, seulement de partir de la piste du peek du 2é lien, mais finalement de s’en écarter et de prendre le max.

@Rabban En réagissant à mon message, tu m’as permis de connecter mes neurones pour ce petit casse-tête, merci. :slight_smile:

J’ai refondu mon source. J’y ai pensé une partie du week-end.

Cela fonctionnait mais je sentais que le max() n’était pas logique ni conforme à ce que tu m’avais expliqué sur les promesses @Mistermick

Là ça semble plus conforme aux promesses c’est-à-dire :

  • de principalement remplacer le max par des attentes de la pile courante c’est-à-dire avec des await sur datasTableCible_PILE, datasTableCible_PILE_NB,
  • en n’oubliant pas l’async au niveau du tableau des variables surveillées par le watcher
  • (et accessoirement en revenant sur watch() au lieu de watchEffect() pour restreindre aux seuls variables sélectionnées.)
let REACT = reactive({
  moninput: "",
  datasTableCible_charge: false,
  datasTableCible: [],
  datasTableCible_PILE: [],
  datasTableCible_PILE_NB: 0,
});
watch(
  () => [REACT.moninput1, REACT.moninput2],
  async ([newRech1, newRech2], [prevRech1, prevRech2]) => {
    let datas_params = {
      recherche_1: newRech1 || "",
      recherche_2: newRech2 || "",
    };

    REACT.datasTableCible_charge = false;

    if (!(datas_params.recherche_1 == "" && datas_params.recherche_2 == "")) {
      REACT.datasTableCible_PILE_NB += 1;

      // je garde en réserve le compteur
      let nb = REACT.datasTableCible_PILE_NB;
      let ret = await getByPostDatas(datas_params); //ramène un { data: [mes données sources] }

      // Les données sont poussées de façon désordonnée par l'Array.push() du fait de
      // l'environnement asynchrone,
      // mais nb restera le même qu'avant l'appel des données.
      // Par exemple selon la durée le dernier et 4e appel ajax
      // va arriver en 2é dans la pile, car rapide, mais avec un idx à 4.


      REACT.datasTableCible_PILE.push([
          Object.assign({ idx: nb }, ret.length == 0 ? { data: [] } : ret),
      ]);

      let pile = await toRaw(REACT.datasTableCible_PILE);
      let num_elem_pile =
        (await toRaw(REACT.datasTableCible_PILE_NB)) || 1;

      let elem_pile_trouve = pile.filter((x) => x[0].idx == num_elem_pile);

      // Je filtre REACT.datasTableCible_PILE pour idx == num_elem_pile
      // et je le recopie dans REACT.datasTableCible

      if (elem_pile_trouve.length != 0) {
        REACT.datasTableCible = [...elem_pile_trouve[0]][0];
      }
    } else {
      REACT.datasTableCible_PILE.length = 0;
      REACT.datasTableCible_PILE_NB = 0;
    }
    REACT.datasTableCible_charge = true;
  }
);


Salut ! ^^

J’essaie de regarder ton code du coup, j’avoue c’est pas évident comme je connais pas ton projet.
Ton problème c’est que tu veux juste le résultat du dernier call ? Ça serait pas plus simple d’abort le précédent call a chaque fois que tu en fais un nouveau ? :slight_smile:

:slight_smile: .

Ben une fois qu’il est parti à la base de données SQL Server, il est parti, je ne peux pas le stopper, à moins de se lancer dans des kill de sessions SQL hasardeux. Exemple

  • m: idx: 1, getdata à venir 240 lignes
  • mo: idx: 2, 120 lignes (idx 1 n’est pas revenu du SQL)
  • mon: idx: 3, 80 lignes (idx 1, 2 ne sont pas revenus du SQL
  • mont: idx: 4, 30 lignes (idx 3 revient,idx 1,2,4 ne sont pas revenus du SQL
  • monta: idx: 5, 0 lignes (idx 5 revient tout de suite, mais il est écrasé par idx3, idx 2 et idx 1 ensuite, ce qui était la source de mon souci comme @Rabban avait remarqué, sauf si je filtre sur l’idx courant.

Peux être parles tu de l’XMLHttpRequest. abort() que je ne connaissais pas, mais en cherchant rapidement ça a l’air récent, et pas forcément applicable sur une requête récurrente.

En plus même si ça marchait du côté navigateur, les données produites par SQL server sont de toute façon en route. À moins de kill la session SQL serveur (mais aussi l’appel côté Flask avec des threads que j’ai déjà utilisé en cas de déconnexion à cause des timeout SQL server très très long).

Pour des requêtes méga longues et très volumineuses ça vaudrait peut-être le coup, mais là je ne suis pas certain que la mise en œuvre soit simple et efficace.

Après il y a sans doute un design pattern en JS: je ne suis pas le seul à me poser la question.

Après je peux me tromper :slight_smile:

Édit :
Là je suis en axios VueJs de base, mais je suppose que le plugin jquery Datatable net, que j’utilise par ailleurs, trouve une solution à ce souci, mais en lisant le code je n’ai pas encore trouvé, d’autant qu’il y a peut-être des fonctionnalités masquées par jquery (*)

(*) D’après ce lien par défaut Datatable semble synchrone. Donc ce n’est pas là que je pourrais trouver une inspiration.

Édit 2:

Mon utilisation d’Internet au sens large me fait dire que l’abort doit être peu utilisé vu les requêtes trop lourdes qui pédalent dans la choucroute en général quand on navigue sur internet… mais après s’il y a une solution mais annuler des requêtes ~ 1 à 2 secondes maxi, autant ignorer le retour ? :slight_smile:

Édit 3 :sweat_smile:

si l’abort fonctionnait au moins pour ignorer il faudrait que mon watcher ait un pointeur sur le dernier appel HttpRequest dans le même watcher.

Ola !
Je pense que la question de Mistermick se recoupe avec la solution proposée par Rabban, et c’est également une solution de ce type que j’utilise :

C’est pour ça qu’en vanilla js pour stopper la requête avant même son départ, la solution du timeout est très simple à mettre en place, comme ça aucune requête ne part tant que la vitesse d’input de l’utilisateur dépasse une valeur donnée.
Par frustration de ne pas trouver d’équivalent non jquery de datatables, j’en ai codé une version en vanilla, et c’est la technique du timeout que j’utilise dans mes champs de recherche.

ps: j’suis un junior hein, mais je me permets de souligner les solutions proposées car elles me paraissent sensées et moins complexes que d’essayer de rattraper des requêtes déjà parties, mais il y a peut être aussi une bonne raison à ça que je n’ai pas saisie, bref, j’espère que ça t’aidera dans ta réflexion et sorry si je suis complètement à côté :wink: