Skip to content

Latest commit

 

History

History
725 lines (564 loc) · 20.7 KB

asincrona.md

File metadata and controls

725 lines (564 loc) · 20.7 KB

Programación asíncrona

Implementando listeners en Java

Listener con un solo control

public class DemoKeyEvents {
   public static void main(String[] args) {
      DemoKeyEventsFrame frame = new DemoKeyEventsFrame();
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.setTitle("DemoKeyEvents");
      frame.pack();
      frame.setVisible(true);
   }
}

class DemoKeyEventsFrame extends JFrame implements KeyListener
{
   private JTextField enterField;

   public DemoKeyEventsFrame() {
      enterField = new JTextField(10);
      enterField.addKeyListener(this);

      JPanel panel = new JPanel();
      panel.add(new JLabel("Enter some text:"));
      panel.add(enterField);
      panel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));

      this.setContentPane(panel);
   }

   public void keyPressed(KeyEvent ke) {
      System.out.println(ke.paramString());

      final KeyStroke BEL = KeyStroke.getKeyStroke(KeyEvent.VK_A, KeyEvent.CTRL_MASK);
      KeyStroke k = KeyStroke.getKeyStroke(ke.getKeyCode(), ke.getModifiers());
      if (k == BEL)
      {
         System.out.println("Ctrl-A pressed!");
         Toolkit.getDefaultToolkit().beep();
      }
   }

   public void keyReleased(KeyEvent ke) {
      System.out.println(ke.paramString());
   }

   public void keyTyped(KeyEvent ke) {
      System.out.println(ke.paramString());
   }
}

Listener con varios controles

public class DemoKeyEvents2 {
   public static void main(String[] args) {
      DemoKeyEvents2Frame frame = new DemoKeyEvents2Frame();
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.setTitle("DemoKeyEvents2");
      frame.pack();
      frame.setVisible(true);
   }
}

class DemoKeyEvents2Frame extends JFrame implements KeyListener {
   private JTextField pro;
   private JTextField con;

   // constructor
   public DemoKeyEvents2Frame() {

      // construct and configure components
      pro = new JTextField(10);
      con = new JTextField(10);

      // add listeners
      pro.addKeyListener(this);
      con.addKeyListener(this);

      // arrange components
      // add components to panels

      JPanel banner = new JPanel();
      banner.add(new JLabel("WHAT'S YOUR OPINION ON ANCHOIVES?"));

      JPanel proPanel = new JPanel();
      proPanel.add(new JLabel("Pro:"));
      proPanel.add(pro);

      JPanel conPanel = new JPanel();
      conPanel.add(new JLabel("Con:"));
      conPanel.add(con);

      // put panels in a content pane panel
      JPanel contentPane = new JPanel();
      contentPane.setLayout(new GridLayout(3, 1));
      contentPane.add(banner);
      contentPane.add(proPanel);
      contentPane.add(conPanel);

      // make panel this JFrame's content pane
      this.setContentPane(contentPane);
   }

   public void keyPressed(KeyEvent ke) {}   // do nothing
   public void keyReleased(KeyEvent ke) {}  // do nothing
   public void keyTyped(KeyEvent ke) {
      Object source = ke.getSource();

      // check if event occurred on pro component
      if (source == pro)
         System.out.println("Pro : " + ke.paramString());

      // check if event occurred on con component
      else if (source == con)
         System.out.println("Con : " + ke.paramString());
   }
}

Listener con clases anónimas

public class DemoKeyEvents3 {
   public static void main(String[] args) {
      DemoKeyEvents3Frame frame = new DemoKeyEvents3Frame();
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.setTitle("DemoKeyEvents3");
      frame.pack();
      frame.setVisible(true);
   }
}

class DemoKeyEvents3Frame extends JFrame {
   private JTextField enterField;

   // constructor
   public DemoKeyEvents3Frame() {
      enterField = new JTextField(10);

      enterField.addKeyListener(new KeyListener() {
          public void keyPressed(KeyEvent ke) {
            System.out.println(ke.paramString());

            final KeyStroke BEL = KeyStroke.getKeyStroke(KeyEvent.VK_A, KeyEvent.CTRL_MASK);
            KeyStroke k = KeyStroke.getKeyStroke(ke.getKeyCode(), ke.getModifiers());
            if (k == BEL) {
               System.out.println("Ctrl-A pressed!");
               Toolkit.getDefaultToolkit().beep();
            }
          }

          public void keyReleased(KeyEvent ke) {
            System.out.println(ke.paramString());
          }

          public void keyTyped(KeyEvent ke) {
            System.out.println(ke.paramString());
          }
      });

      JPanel panel = new JPanel();
      panel.add(new JLabel("Enter some text:"));
      panel.add(enterField);
      panel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));

      this.setContentPane(panel);
   }

}

Listener con adaptador

  • No hay que redefinir todos los métodos de KeyListener
  • Los métodos de KeyAdapter están definidos pero vacíos
class DemoKeyEvents4Frame extends JFrame {
   private JTextField enterField;

   // constructor
   public DemoKeyEvents4Frame()
   {
      enterField = new JTextField(10);

      enterField.addKeyListener(new KeyAdapter() {
          @Override
          public void keyPressed(KeyEvent ke) {
            System.out.println(ke.paramString());

            final KeyStroke BEL = KeyStroke.getKeyStroke(KeyEvent.VK_A, KeyEvent.CTRL_MASK);
            KeyStroke k = KeyStroke.getKeyStroke(ke.getKeyCode(), ke.getModifiers());
            if (k == BEL) {
               System.out.println("Ctrl-A pressed!");
               Toolkit.getDefaultToolkit().beep();
            }
          }

          @Override
          public void keyReleased(KeyEvent ke) {
            System.out.println(ke.paramString());
          }

          @Override
          public void keyTyped(KeyEvent ke) {
            System.out.println(ke.paramString());
          }
      });

      JPanel panel = new JPanel();
      panel.add(new JLabel("Enter some text:"));
      panel.add(enterField);
      panel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));

      this.setContentPane(panel);
   }

}

Listener con lambdas y dispatch table

import java.awt.BorderLayout;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.util.HashMap;
import javax.swing.JFrame;
import javax.swing.JTextField;

public class Test {

    @FunctionalInterface
    interface KeyAction {
        public void doAction(); //un sólo método abstracto
    }

    public static void main( String[] args) {

        HashMap<Integer, KeyAction> keyDispatcher = new HashMap<Integer, KeyAction>();

        //Crear instancias de FunctionalInterface mediante lambdas
        keyDispatcher.put(KeyEvent.VK_W, () -> moveUp());
        keyDispatcher.put(KeyEvent.VK_S, () -> moveDown());
        keyDispatcher.put(KeyEvent.VK_A, () -> moveLeft());
        keyDispatcher.put(KeyEvent.VK_D, () -> moveRight());

        // Using a JTextField out of simplicity
        JTextField field = new JTextField();
        field.addKeyListener(new KeyAdapter() {
            @Override
            public void keyPressed(KeyEvent arg0) {
                try{
                    keyDispatcher.get(arg0.getKeyCode()).doAction();
                } catch (NullPointerException e) {
                    System.out.println("That button doesn't do anything yet...");
                }
            }
        });

        JFrame frame = new JFrame("Listener Frame");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        frame.add(field, BorderLayout.NORTH);
        frame.pack();
        frame.setVisible(true);
    }

    private static void moveUp() {
        System.out.println("Moving up");
    }
    private static void moveDown() {
        System.out.println("Moving down");
    }
    private static void moveLeft() {
        System.out.println("Moving left");
    }
    private static void moveRight() {
        System.out.println("Moving right");
    }
}

Ejemplo de listener con y sin lambdas

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

/**
 * This simple Swing program demonstrates how to use Lambda expressions in
 * action listener.
 *
 * @author www.codejava.net
 */
public class ListenerLambdaExample extends JFrame {

    private JButton button = new JButton("Click Me!");

    public ListenerLambdaExample() {
        super("Listener Lambda Example");

        getContentPane().setLayout(new FlowLayout());
        getContentPane().add(button);

        // Java 7 - tradicional, sin lambdas
        button.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent evt) {
                System.out.println("Handled by anonymous class listener");
            }
        });

        // Java 8 - con lambdas
        button.addActionListener(e -> System.out.println("Handled by Lambda listener"));

        button.addActionListener(e -> {
            System.out.println("Handled Lambda listener");
        });

        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setSize(300, 100);
        setLocationRelativeTo(null);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                new ListenerLambdaExample().setVisible(true);
            }
        });
    }
}

Ejercicio

  • Refactorizar DemoKeyEvents2 con adaptador, interfaces funcionales (lambdas) y tabla de dispatch

Modelos de programación asíncrona

La programación asíncrona promueve la definición de operaciones no bloqueantes.

modelos de ejecución

por Javier Vélez Reyes, Programación asíncrona en JavaScript

Las funciones no bloqueantes afectan a:

  • El estado del programa
  • La lógica de continuación del programa
programación secuencial programación asíncrona
Modelo de programación secuencial Modelo de programación asíncrona

Modelo de paso de continuaciones

Aumentar la aridad de la función no bloqueante en 1 argumento adicional, donde se indica la lógica de continuación.

Callbacks

La lógica de continuación se indica mediante una función de retrollamada o callback.

Paso de callback

Ejemplos: implementaciones de listener del ejercicio introductorio

  • Con clases anónimas
  • Con adaptadores
  • Con funciones anónimas o lambdas

Ejemplo: Ajax + jQuery callbacks

Ejemplo: callback en TypeScript

function delayedResponseWithCallback(callback: Function) {
    function delayedAfterTimeout() {
        console.log(`delayedAfterTimeout`);
        callback();
    }
    setTimeout(delayedAfterTimeout, 1000);
}

function callDelayedAndWait() {
    function afterWait() {
        console.log(`afterWait`);
    }
    console.log(`calling delayedResponseWithCallback`);
    delayedResponseWithCallback(afterWait);
    console.log(`after calling delayedResponseWithCallback`);
}
callDelayedAndWait();

Salida:

calling delayedResponseWithCallback
after calling delayedResponseWithCallback
delayedAfterTimeout
afterWait

El uso de callbacks hace el código complejo, repetitivo y difícil de entender, especialmente cuando el tamaño del código crece.

Thunks

thunk = subrutina empleada para inyectar un cómputo adicional en otra subrutina

  • Difieren un cómputo hasta que éste se necesita
  • Insertan una operación al principio o al final de otra
  • Aumentan la aridad de la función no bloqueante en una fase adicional de evaluación parcial para recibir la lógica de continuación
  • Un thunk puede guardar su estado final para evitar recalcularlo (memoization)

Paso de thunk

Lenguajes: Ruby

  • Cualquier función en Ruby puede recibir un bloque do... end como argumento adicional (no explícito) a la llamada
  • Un thunk es como un bloque con un yield al final

Promesas

Modelo de futuros y promesas

  • Futuro: marcador de posición (placeholder), de solo lectura, para una variable que representa el resultado de un cómputo asíncrono
  • Promesa: contenedor escribible (solo para inicialización), que fija el valor de un futuro.

En programación funcional, los futuros y promesas sirven para desacoplar un valor (el futuro) de cómo éste se calculó (la promesa), permitiendo así la paralelización de los cálculos.

Promesas

El cliente recibe como respuesta inmediata una abstracción de datos (la promesa) que representa un compromiso de valor futuro, con inyectores (then, catch) para incluir la lógica de continuación.

Se pueden encadenar cálculos usando futuros computables o escuchables, que sirven para indicar a un thread que ejecute una determinada tarea y, cuando termine, se dirija a hacer otra tarea usando el resultado de la tarea anterior.

Lenguajes: TypeScript

En TypeScript, una Promise<T> es un objeto que, en su creación, recibe una función (anónima o no) que acepta dos callbacks (resolve y reject) y devuelve un valor de tipo T.

Ejemplo: definición de promesa que resuelve
function delayedPromise(): Promise<void> {
    return new Promise<void>
    (
        // función anónima con dos callbacks como argumentos
        (   resolve : () => void,   //callback para resolver
            reject: () => void      //callbak para rechazar
        ) => {
            // función que resuelve tras un timeout de 1 sg.
            function afterTimeout() {
                resolve();
            }
            setTimeout(afterTimeout, 1000);
        }
    );
}
Ejemplo: uso para resolver
function callDelayedPromise() {
    console.log(`calling delayedPromise`);
    delayedPromise().then(
        // función anónima a llamar cuando se resuelva la promesa
        () => { console.log(`delayedPromise.then()`) }
    );
}

callDelayedPromise();

Salida:

calling delayedPromise
delayedPromise.then()
Ejemplo: definición de promesa que rechaza
function errorPromise(): Promise<void> {
    return new Promise<void>
    (
        (   resolve: () => void,
            reject: () => void
        ) => {
            reject();
        }
    );
}
Ejemplo: uso para rechazar
function callErrorPromise() {
    console.log(`calling errorPromise`);
    errorPromise().then(
        () => { console.log(`no error.`) }
    ).catch(
        () => { console.log(`an error occurred`)}
    );
}

callErrorPromise();

Salida:

calling errorPromise
an error occurred

Comparación callbacks-promises en TypeScript

Mecanismo de los callback
function standardCallback() {
    function afterCallbackSuccess() {
    // execute this code
    }
    function afterCallbackError() {
    // execute on error
    }
    // invoke async function
    invokeAsync(afterCallbackSuccess, afterCallbackError);
}
Sintaxis (fluent) de las promesas:
function usingPromises() {
    // invoke async function
    delayedPromise().then(
        () => {
            // execute on success
        }
    ).catch (
        () => {
            // execute on error
        }
    );
}

Async/await

  • El prefijo await hace que se espere a que se llame a la función asíncrona antes de continuar con la ejecución del programa.
  • Esto genera un flujo de ejecución de la lógica del programa más fácil de leer y de seguir, pausando la ejecución hasta que se cumpla la promesa.
function awaitDelayed(): Promise<void> {
    return new Promise<void> (
        (   resolve: () => void,
            reject: () => void ) =>
            {
                function afterWait() {
                    console.log(`calling resolve`);
                    resolve();
                }
                setTimeout(afterWait, 1000);
            }
        );
}

async function callAwaitDelayed() {
    console.log(`call awaitDelayed`);
    await awaitDelayed();
    console.log(`after awaitDelayed`);
}
callAwaitDelayed();

Salida:

call awaitDelayed
calling resolve
after awaitDelayed
Comparación sintaxis then/catch y async/await

Promesas que usan then/catch para definir funciones anónimas a llamar dependiendo del resultado éxito/fracaso de la ejecución:

function simplePromises() {
    // invoke async function
    delayedPromise().then(
        () => {
            // execute on success
        }
    ).catch (
        () => {
            // execute on error
        }
    );
    // code here does NOT wait for async call
}

Sintaxis async/await, más legible y menos propensa a errores:

async function usingAsyncSyntax() {
    try {
        await delayedPromise();
        // execute on success
    } catch(error) {
        // execute on error
    }
    // code here waits for async call
}

Lenguajes: Java

En Java hay definida una interfaz explícita para los futuros:

Ejemplo: Future en Java
// Callable<V> = Interfaz funcional que representa a una operación sin args y que devuelve un resultado de tipo V (permite checked exceptions)
public static class MyCallable implements Callable<Integer> {
    @Override
    public Integer call() throws Exception {
        Thread.sleep(1000);
        return 1;
    }
}

public static void main(String[] args) throws Exception{
    ExecutorService exec = Executors.newSingleThreadExecutor();
    Future<Integer> f = exec.submit(new MyCallable());
    System.out.println(f.isDone()); //False
    System.out.println(f.get()); //Waits until the task is done, then prints 1
}
Ejemplo: CompletableFuture en Java
// Supplier<T> = Interfaz funcional que representa a una operación sin args y que devuelve un resultado de tipo T (no permite checked exceptions)
public static class MySupplier implements Supplier<Integer> {
    @Override
    public Integer get() {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            //Do nothing
        }
        return 1;
    }
}

public static class PlusOne implements Function<Integer, Integer> {
    @Override
    public Integer apply(Integer x) {
        return x + 1;
    }
}

public static void main(String[] args) throws Exception {
    ExecutorService exec = Executors.newSingleThreadExecutor();
    CompletableFuture<Integer> f = CompletableFuture.supplyAsync(new MySupplier(), exec);
    System.out.println(f.isDone()); // False
    CompletableFuture<Integer> f2 = f.thenApply(new PlusOne());
    System.out.println(f2.get()); // Waits until the "calculation" is done, then prints 2
}

Lenguajes, JavaScript

LECTURA recomendada: Promises/A+: An open standard for sound, interoperable JavaScript promises.

Modelos basados en eventos

Las operaciones disparan eventos de diferentes tipos, que son escuchados por los manejadores (listeners) de eventos, que los clientes han registrado en un bus de eventos

Eventos

Eventos

Streams

Los datos fluyen por pipelines y se consumen siguiendo modelos push o pull

Streams

Observables

LECTURA recomendada: The introduction to Reactive Programming you've been missing (by @andrestaltz)