AWS Lambda

Etter å ha knotet med AWS Lambda i over en måned har jeg lært mye nyttig som kanskje kan hjelpe andre som skal bruke denne superkjekke tjenesten.

(Det er lite kodeeksempler i AWS’ API-dokumentasjon så hver gang man skal ta i bruk ny funksjonalitet blir det mye prøving og feiling kombinert med googling.)

Første problemet man som regel opplever er at ens egen Lambda-funksjon avslutter før kall til andre AWS-tjenester returnerer et resultat (eller error). Dette er simpelthen fordi kallet er asynkront og kanskje trenger flere hundre millisekunder før det fullfører.

For å vente på asynkrone kall har man heldigvis flere muligheter.

Promise

En mye brukt, men fortsatt ganske ny løsning, er promise:

const AWS = require('aws-sdk');

exports.handler = async () => {

   var dynamodb = new AWS.DynamoDB(...);
   await dynamodb.putItem(...).promise().then(function() {
      // Kode for suksess her
   }).catch(function(error) {
      // Kode for feil her
   });

   // Synkron kode fortsetter her
};

Her er det await og promise som styrer showet. Og på promise lenkes then også catch. Etterpå kan man fortsette med annen synkron kode. Dette er en veldig ryddig måte å gjøre det på. Dessverre er det ikke alle AWS API-er som har promise.

Om det er flere asynkrone kall som skal gjennomføres bør man fjerne await over og vente på kallene samtidig med Promise.all:

...
var dynamodb = new AWS.DynamoDB(...);
var promise1 = dynamodb.putItem(...).promise();
var promise2 = <annet asynkront API-kall her>.promise();
// o.s.v.
Promise.all([promise1, promise2, ...])

Også henger man på then og catch her i stedet for å ha det på hver promise over. Men dette er selvsagt bare mulig så lenge kallene kan kjøres uavhengig av hverandre.

Hendelser

Man har også anledning til å bruke hendelser:

const AWS = require('aws-sdk');

exports.handler = () => {

   var dynamodb = new AWS.DynamoDB(...);
   var kall = dynamodb.putItem(...);
   kall.on('success', function(respons) {
      // Kode for suksess her
   });
   kall.on('error', function(error) {
      // Kode for feil her
   });
   kall.send();

   // Synkron kode
   // fortsetter her
};

Tilbakekall

Alternativt har man bruk av tilbakekall (altså «callback» på engelsk), men hvor man dessverre havner i "callback helvete" hvis man skal gjøre mange kall:

const AWS = require('aws-sdk');

exports.handler = () => {

   var dynamodb = new AWS.DynamoDB(...);
   dynamodb.putItem(..., function(error, data) {
      if (error) {
         // Kode for feil her
      } else {
         // Kode for suksess her
      }
   });
};

Problemet med alle eksemplene over er at det blir unødig venting. Og siden man betaler for hver påbynte bolk med 100 millisekunder er det viktig å begrense ventingen så mye som mulig.

Eksemplet nedenfor viser den raskeste måten jeg har funnet til nå:

const AWS = require('aws-sdk');

exports.handler = (hendelse, kontekst, tilbakekall) => {
   kontekst.callbackWaitsForEmptyEventLoop = false;

   var dynamodb = new AWS.DynamoDB(...);
   dynamodb.putItem(..., function(error) {
      if (error) {
         // Kode for feil her
      } else {
         // Kode for suksess her
      }

      tilbakekall(null, ...);
   });
};

Gjentatte tester viste at tidsforbruket minst ble halvert.

Å eksperimentere med AWS Lambda er noe man aldri blir ferdig med ..

TBC

Webserver i Node.js

Her følger en enkel webserver i Node.js som kan tilpasses slik man vil:

var html = "";
  html += "<!doctype html>\n";
  html += "<html>\n";
  html += "<head>\n";
  html += "<title>TITTEL</title>\n";
  html += "<meta charset='utf-8'>\n";
  html += "</head>\n";
  html += "<h1>TITTEL</h1>\n";
  html += "<p>INNHOLD</p>\n";
  html += "</html>\n";

var http = require("http");

var server = http.createServer(function(forespørsel, respons) {

  // Mal
  var side = html;

  // Behandling av forespørsel
  if (forespørsel.url === "/") {
    side = side.split("TITTEL").join("Front");
    side = side.split("INNHOLD").join("Hei! Dette er frontsiden.");

  } else if (forespørsel.url === "/kontakt") {
    side = side.split("TITTEL").join("Kontakt");
    side = side.split("INNHOLD").join("E-post: post@ovebakken.no");

  } else {
    side = side.split("TITTEL").join("Feil");
    side = side.split("INNHOLD").join("Forespurt side eksisterer ikke.");
  }

  // Send innhold
  respons.writeHead(200, {"Content-Type": "text/html"});
  respons.write(side);
  respons.end();
});
server.listen(80);

HTML-koden kan endres til hva som helst, og det er enkelt å legge til eller fjerne sider.

Portnummer her er 80, men kan også endres så lenge man velger en port som er ledig.

TBC

Funksjoner i Vue.js

Alle apps har datamedlemmer som brukes i funksjoner for å lage nytt innhold.

methods

Her følger et eksempel hvor to tall skal legges sammen:

Tall 1: <input type="text" v-model:value="tall1">
Tall 2: <input type="text" v-model:value="tall2">
Svar: <input v-bind:value="svar()">

Det er to felt for å taste inn tall, og et felt for å vise svaret.

Funksjonen svar tar seg av arbeidet:

var app = new Vue({
  el: "#app",
  data: {
    tall1: 0,
    tall2: 0
  },
  methods: {
    svar: function() {
      return parseFloat(this.tall1) + parseFloat(this.tall2);
    }
  }
});

computed

Alternativt kan man gjøre som følger:

var app = new Vue({
  el: "#app",
  data: {
    tall1: 0,
    tall2: 0
  },
  methods: {
  },
  computed: {
    svar: function() {
      return parseFloat(this.tall1) + parseFloat(this.tall2);
    }
  }
});

Her er svar flyttet til computed i stedet for, dette muliggjør følgende:

Tall 1: <input type="text" v-model:value="tall1">
Tall 2: <input type="text" v-model:value="tall2">
Svar: <input v-bind:value="svar">

Her henvises det til svar som om dette er et datamedlem. Dette blir litt mer elegant.

TBC

Egne komponenter i Vue.js

Ofte er det kjekt å lage sine egne komponenter, som deretter kan brukes igjen og igjen.

Enveis

Her følger en enkel komponent som gir lite nytte, men som gjør oppsettet forståelig:

<tittel v-bind:tekst=tittel></tittel>
Vue.component("tittel", {
  props: ["tekst"],
  template: "<h1>{{tekst}}</h1>"
});
..
var app = new Vue({
  data: {
    tittel: "Min tittel her"
  }
});

I praksis er <h1> byttet med <tittel>, og når scriptet kjører byttes det bare tilbake.

Toveis

En mye nyttigere komponent er en som tar i mot informasjon:

Opplysning 1: <inndata v-model="opplysning1"></inndata>
Opplysning 2: <inndata v-model="opplysning2"></inndata>
..
Svar: <inndata v-bind="svar"></inndata>
Vue.component("inndata", {
  props: ["tekst"],
  template: `<input type="text" v-bind:value="tekst" v-on:input="$emit('input', $event.target.value)" >`
});

Her får man en fiks ferdig standard inndata-komponent som kan gjenbrukes alle steder. Man slipper da noe av den repetive kodeskrivingen og ekstraarbeidet når noe skal forandres senere.

TBC

Viktigste npm-kommandoer

Her følger en oversikt over de viktigste npm-kommandoene. Alle disse må kjøres fra prosjektets rotmappe hvis ikke annet er nevnt.

Oppsett

npm init

Man kjører denne kommandoen for et nytt prosjekt når man ønsker litt hjelp med å opprette en ny package.json:

Denne filen inneholder navn, beskrivelse, o.s.v. for prosjektet og brukes for å holde styr på alle avhengigheter som oppstår senere.

npm install pakkenavn –save

Ved å kjøre denne kommandoen lastes pakken ned og legges i en mappe ved navn node_modules. I tillegg lagres pakken som en avhengighet i package.json p.g.a. –save.

npm install

Ved å kjøre denne kommandoen opprettes node_modules med innhold, takket være package.json og dens lagrede avhengigheter.

TBC

Kom i gang med Vue.js

Om du ønsker å komme i gang med Vue.js er dette enkelt. I motsetning til hvordan ståa er for de fleste andre populære bibliotek/rammeverk som f.eks. Angular.js og Ember.js.

Man trenger kun en moderne nettleser, en teksteditor, samt ei HTML-fil med litt grunnleggende startinnhold:

<html>
  <head>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
  </head>
  <body>
    HTML-kode her ..
    <script>JavaScript-kode her ..</script>
  </body>
</html>

Det er riktignok anbefalt å ha litt kjennskap til HTML og JavaScript før man begynner, men dette kan man lære seg samtidig hvis man bare prøver.

Nedenfor følger de viktigste kodeeksemplene som er å finne i oppstartsguiden til Vue.js. Først er det litt HTML-kode, så JavaScript. Alle eksemplene er rimelig selvforklarende og gir et godt utgangspunkt for hvordan man skal ta i bruk en bestemt funksjonalitet.

Innhold

Enveis

<div id="app">
  {{ melding }}
</div>
var app = new Vue({
  el: '#app',
  data: {
    melding: 'Hallo Vue!'
  }
})

Toveis

<div id="app">
  {{ melding }}
  <input v-model="melding">
</div>
var app = new Vue({
  el: '#app',
  data: {
    message: 'Hallo Vue!'
  }
})

Kontroll

if

<div id="app">
  <span v-if="synlig">Nå ser du meg</span>
</div>
var app = new Vue({
  el: '#app',
  data: {
    synlig: true
  }
})

for

<div id="app">
  <ol>    
    <li v-for="oppgave in oppgaver">
      {{ oppgaver.tekst }}
    </li>
  </ol>
</div>
var app = new Vue({
  el: '#app',
  data: {
    oppgaver: [
      { tekst: 'Lær JavaScript' },
      { tekst: 'Lær Vue' },
      { tekst: 'Lag noe kult!' }
    ]
  }
})

Hendelser

click

<div id="app">
  {{ melding }}
  <button v-on:click="reverserMelding">Reverser melding</button>
</div>
var app = new Vue({
  el: '#app',
  data: {
    melding: 'Hallo Vue.js!'
  },
  methods: {
    reverserMelding: function () {
      this.melding = this.melding.split('').reverse().join('');
    }
  }
})