Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Aspect Oriented Programming (AOP) in JavaScript #99

Open
tonix-tuft opened this issue Sep 22, 2020 · 0 comments
Open

Aspect Oriented Programming (AOP) in JavaScript #99

tonix-tuft opened this issue Sep 22, 2020 · 0 comments

Comments

@tonix-tuft
Copy link

tonix-tuft commented Sep 22, 2020

Ciao a tutti,

propongo un talk sull'AOP (Aspect Oriented Programming) in JavaScript, ho sviluppato una libreria apposita (AOPlà) che permette di facilitare il lavoro con i cosiddetti "cross-cutting concerns" e mi piacerebbe condividere la mia esperienza, sempre che l'argomento possa interessare a qualcuno.

Gli argomenti trattati:

  • Che cos'è l'AOP e quando può aver senso utilizzare questo paradigma lavorando con codice OOP;
  • Come creare aspetti utilizzando AOPlà e implementare funzionalità riusabili come caching, logging, ecc...;
  • Quali alternative ad AOPlà sono presenti ad oggi sempre nel mondo JavaScript e quali sono le principali differenze;
  • Under the hood, com'è implementata AOPlà e che cosa c'è effettivamente sotto il cofano: Proxy composition tramite ES6 Proxy API con pigretto, i decorator in JavaScript (legacy/stage-1 e stage-2), design pattern come Chain of Responsibility;

Di seguito un piccolo esempio di come si può implementare una funzionalità di caching (molto semplificata) senza e con la libreria.

Senza AOPlà:

class SomeService {
  /**
   * Sample cache.
   */
  cache = void 0;

  cacheInterval = void 0;

  /**
   * Sample method which performs a heavy computation, each time caching the result for subsequent calls
   * with a TTL (Time To Live) of 5 seconds.
   */
  async performHeavyComputation() {
    if (this.cache) {
      // Cache hit:
      console.log("Cache hit!");
      await this.cache;
      return;
    }
    // Cache miss:
    console.log("Cache miss...");

    // Business logic code:
    await new Promise(
      (resolve) => setTimeout(resolve, 8000) // Heavy computation timeout.
    );

    // Ugly cache management code mixed with business logic code:
    console.log("Caching.");
    this.cache = Promise.resolve();
    this.cacheInterval && clearInterval(this.cacheInterval);
    this.cacheInterval = setTimeout(() => {
      // Invalidating the cache after 5 seconds.
      console.log("Cache invalidation.");
      this.cache = void 0;
    }, 5000);
  }
}

// Run:
(async () => {
  const someService = new SomeService();

  // Cache miss.
  await someService
    .performHeavyComputation()
    .then(() => console.log(`Heavy computation completed.`));
  // Cache hit.
  await someService
    .performHeavyComputation()
    .then(() => console.log(`Heavy computation completed.`));

  // Waiting TTL expiration.
  await new Promise((resolve) => setTimeout(resolve, 6000));

  // Cache miss.
  await someService
    .performHeavyComputation()
    .then(() => console.log(`Heavy computation completed.`));
  // Cache hit.
  await someService
    .performHeavyComputation()
    .then(() => console.log(`Heavy computation completed.`));
})();

Con AOPlà:

import AOPla, { tag, aroundCall } from "aopla";

const Cacheable = tag("Cacheable");

class CacheAspect {
  cache = void 0;

  cacheInterval = void 0;

  @aroundCall(Cacheable)
  async advice({ proceed }) {
    if (this.cache) {
      // Cache hit:
      console.warn("Cache hit!");
      await this.cache;
      return;
    }
    // Cache miss:
    console.warn("Cache miss...");
    await proceed();
    // Cache management code only.
    console.warn("Caching.");
    this.cache = Promise.resolve();
    this.cacheInterval && clearInterval(this.cacheInterval);
    this.cacheInterval = setTimeout(() => {
      // Invalidating the cache after 5 seconds.
      console.warn("Cache invalidation.");
      this.cache = void 0;
    }, 5000);
  }
}

AOPla.registerAspects(CacheAspect);

class SomeService {
  /**
   * Sample method which performs a heavy computation, no other fancy code.
   */
  @Cacheable // Tagging this method as cacheable.
  async performHeavyComputation() {
    await new Promise(
      (resolve) => setTimeout(resolve, 8000) // Heavy computation timeout.
    );
  }
}

// Run:
(async () => {
  const someService = new SomeService();

  // Cache miss.
  await someService
    .performHeavyComputation()
    .then(() => console.log(`Heavy computation completed.`));
  // Cache hit.
  await someService
    .performHeavyComputation()
    .then(() => console.log(`Heavy computation completed.`));

  // Waiting TTL expiration.
  await new Promise((resolve) => setTimeout(resolve, 6000));

  // Cache miss.
  await someService
    .performHeavyComputation()
    .then(() => console.log(`Heavy computation completed.`));
  // Cache hit.
  await someService
    .performHeavyComputation()
    .then(() => console.log(`Heavy computation completed.`));
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant