5
votes

Angular 5 - Promise vs Observable - contexte de performance

J'ai un site Angular 5 qui reçoit des données d'une API REST, quelque chose comme 1 à 4 requêtes à l'API chaque page, et ce qui se passe, c'est que les requêtes prennent parfois du temps (et parfois pas).

Désormais, toutes les requêtes sont exécutées dans une seule fonction en utilisant Observable :

return this.http.post(url, {headers: this.header})
        .map(res => res.json())      
        .catch(this.handleError)

Ma question est la suivante: est-ce que le processus lent se produit parce qu’un observable est utilisé? Les promesses seront-elles meilleures pour les performances? Ou n'y a-t-il aucune différence entre un observable et une promesse dans un contexte de performance?


5 commentaires

Je n'ai jamais vu de problème de performance avec angular. pour être sûr que vous pouvez essayer en forçant l'opérateur toPromise ()


jetez un œil à cela peut être utile stackoverflow.com/a/43828666/3585496


Je suis presque sûr que dans ce cas d'utilisation, il ne devrait pas y avoir de grande différence entre une promesse et une observable (en termes de performances). Vous pouvez utiliser une promesse ici, car vous ne profitez pas des choses qu'un observable vous offre. Jetez un œil aux DevTools dans l'onglet réseau et voyez si vous pouvez déterminer si votre point de terminaison api ralentit parfois.


Vous dites qu'il est traité lentement. Quelle partie est lente et comment le savez-vous? S'agit-il de res.json () ?


En complément de mon commentaire: sans regarder uniquement Promise et Observable et regarder à la place HttpClient.get et fetch J'ai fait quelques tests stackblitz.com/edit/typescript-salbr6 Comme vous pouvez le voir, ceci fluctue fortement et je crois honnêtement que changer 1 à 4 demandes pour utiliser l'autre méthode ne change rien de ce que nous pourrions voir de nos yeux nus. Si les requêtes prennent plus de temps, c'est votre serveur ou la connexion, mais pas l'implémentation de la façon dont vous obtenez les données (en utilisant http, dans ce contexte).


4 Réponses :


4
votes

parce que vous me posez des questions. J'ai créé les mêmes tests qui ressemblent à ceci:

observable: 160.162353515625ms
promise: 213.40625ms

et le résultat ressemble à ceci (peut être différent sur votre navigateur / configuration mais la proportion doit être la même:

console.time('observable');
for(let i = 0; i < 10000; i++) {
  let user$ = Observable.create((o) => {
    setTimeout(() => {
      o.next({
        name: 'observable'
      });
    });
  });

  user$.subscribe(user => {
    // do something. Prefer not console.log because is ressource consuming.
  });
}
console.timeEnd('observable');

console.time('promise');
for(let i = 0; i < 10000; i++) {
  new Promise((resolve) => {
    setTimeout(() => resolve({
      name: 'promise'
    }))
  }).then(user => {
    // do something. Prefer not console.log because is ressource consuming.
  });
}
console.timeEnd('promise');

EDIT:

Une autre implémentation avec les deux traitements asynchrones à l'intérieur:

observable: 34.060791015625ms
promise: 103.4609375ms

Les résultats sont proches mais la course est gagnée par observable.

console.time('observable');
for(let i = 0; i < 10000; i++) {
  let user$ = of({
    name: 'yanis-git'
  });

  user$.subscribe(user => {
    // do something. Prefer not console.log because is ressource consuming.
  });
}
console.timeEnd('observable');

console.time('promise');
for(let i = 0; i < 10000; i++) {
  new Promise((resolve) => {
    resolve({
      name: 'promise'
    });
  }).then(user => {
    // do something. Prefer not console.log because is ressource consuming.
  });
}
console.timeEnd('promise');

échantillon en direct

si vous voulez vérifier sur stackblitz, veuillez utiliser une vraie console de navigateur pour voir la sortie du minuteur


4 commentaires

Les promesses de l'extrait de code se résoudront de manière asynchrone - sous forme de microtasques - alors que les observables sont synchrones, de sorte que la comparaison effectuée ici est erronée.


Basez sur cette réponse semble que vous avez raison, j'ai modifié ma réponse pour encapsuler les deux appels dans setTimeout , pensez-vous que c'est mieux maintenant?


Cela semble être un bon test, mais pour une raison quelconque, j'obtiens des réponses opposées aux vôtres, montrant que cette promesse est la plus efficace. Lorsque j'exécute le test dans Chrome, en visitant directement cette page et en ouvrant la console: typescript-gnuyxt.stackblitz.io J'obtiens ces résultats: observable: 246.86181640625ms index.ts: 29 promesse: 165.449951171875ms En les exécutant à plusieurs reprises, je trouve toujours Observable prenant plus de temps que Promise, ce qui semble différent de vos résultats.


J'ai regardé de plus près le test de performances que vous avez effectué et j'ai remarqué qu'il ne prenait pas en compte le temps de résolution des fonctions asynchrones, j'ai donc forké votre code et fait un autre test ( stackoverflow.com/a/60157433/4386681 ) pour inclure la résolution, ce qui montre que les promesses sont plus performantes.



3
votes

D'après mes tests, une Promise est plus performante qu'une Observable.

Je pense que Yanis-git test est un bon début, mais ne montre qu'une partie de l'image. Il ne calcule que le début de la promesse ou de l'observable, mais ne compte pas le temps pour sa résolution.

Voici le code que j'ai modifié pour prendre également en compte la résolution des fonctions asynchrones: https://stackblitz.com/edit/typescript-xhhseh?file=index. ts

promise: 232ms - timer ended 
observable: 319ms - timer ended

Lorsque j'exécute le test dans Chrome (sur Mac), en visitant cette page directement et en ouvrant la console: https://typescript-xhhseh.stackblitz.io/ J'obtiens ces résultats:

promise: 279.65185546875ms
observable: 552.891845703125ms

Et un résultat très similaire dans Firefox:

import { of, Observable, zip } from 'rxjs';
console.clear();

function testPromise(){
  console.time('promise');
  const promiseAry = [];
  for(let i = 0; i < 10000; i++) {
    promiseAry[i] = new Promise((resolve) => {
      setTimeout(() => resolve({
        name: 'promise'
      }))
    }).then(user => {
      // do something. Prefer not console.log because is ressource consuming.
    });
  }
  Promise.all(promiseAry).then(() =>{
    console.timeEnd('promise');

    // test Observables after Promises have completed
    testObservable();
  })
}

function testObservable(){
  console.time('observable');
  const observeAry = [];
  for(let i = 0; i < 10000; i++) {
    observeAry[i] = Observable.create((o) => {
      setTimeout(() => {
        o.next({
          name: 'observable'
        });
      });
    });

    observeAry[i].subscribe(user => {
      // do something. Prefer not console.log because is ressource consuming.
    });
  }
  let source$ = zip(...observeAry);
  source$.subscribe(([weatherInfo, tweetInfo]) =>
    console.timeEnd('observable')
  );

}

testPromise();

En les exécutant à plusieurs reprises, je trouve toujours Observable qui prend plus de temps que Promise, ce qui est logique d'autant plus que les promesses sont maintenant natifs de JavaScript, alors que les Observables ne le sont pas, ils ne semblent donc pas aussi performants.

Un merci spécial à Yanis-git pour avoir proposé le test original que j'ai fourché.


0 commentaires

0
votes

Ce n'est pas vraiment un bon exemple du dernier commentaire. Une utilisation plus correcte de RxJS dans ce schéma serait dans cet exemple modifié:

https://stackblitz.com/edit/typescript-uajatd

Performances du navigateur de travail: https://typescript-uajatd.stackblitz.io

Et nous pouvons voir que RxJS remporte principalement la course :)


0 commentaires

0
votes

La règle d'or du dépannage des performances est la suivante: toujours mesurer; ne sautez jamais aux conclusions.

Commencez par reproduire un scénario où la page s'exécute lentement. Vérifiez l'onglet Réseau dans la console développeur de votre navigateur. Combien de temps prend chaque demande réseau? Les demandes s'exécutent-elles en parallèle ou y a-t-il une formation en escalier, où chaque demande n'est pas lancée tant que la précédente n'est pas terminée? Y a-t-il une longue pause entre les demandes, ou chacune d'entre elles commence-t-elle immédiatement lorsque la précédente se termine? La page apparaît-elle chargée immédiatement lorsque la dernière demande est terminée?

Si vous ne parvenez pas à reproduire le problème, envisagez d'utiliser un outil de surveillance, comme sentry.io, pour vous aider à collecter des données. Analysez les données et découvrez ce qui prend si longtemps. Existe-t-il un exemple particulier d'enregistrement dans votre application qui déclenche cette condition?

Vous devriez également consulter les journaux des applications côté serveur. Combien de temps faut-il au serveur pour répondre à chaque demande?

Si vous avez besoin de savoir en détail ce que fait votre code rxjs, pensez à utiliser cet outil: https://github.com/cartant/rxjs-spy

J'ai trouvé cela très utile pour le développement local.

Notez que rxjs-spy introduit une surcharge de performances importante et n'est PAS adapté à une utilisation en production.


0 commentaires