sql >> Databáze >  >> NoSQL >> MongoDB

Jednoduchá aplikace Node/Express, způsob funkčního programování (Jak zvládnout vedlejší účinky v JavaScriptu?)

Nebudete se moci zcela vyhnout vedlejším účinkům, ale můžete vyvinout určité úsilí, abyste je co nejvíce abstrahovali, kde je to možné.

Například expresní rámec je ze své podstaty nezbytný. Spouštíte funkce jako res.send() zcela pro jejich vedlejší účinky (většinu času vás ani nezajímá jeho návratnost).

Co byste mohli udělat (kromě použití const pro všechna vaše prohlášení pomocí Immutable.js datové struktury, Ramda , zapsat všechny funkce jako const fun = arg => expression; místo const fun = (arg) => { statement; statement; }; atd.) by bylo udělat malou abstrakci o tom, jak Express obvykle funguje.

Můžete například vytvořit funkce, které převezmou req jako parametr a vrátí objekt, který obsahuje stav odpovědi, hlavičky a proud, který má být veden jako tělo. Tyto funkce by mohly být čistými funkcemi v tom smyslu, že jejich návratová hodnota závisí pouze na jejich argumentu (objektu požadavku), ale stále byste potřebovali nějaký obal, abyste skutečně odeslali odpověď pomocí inherentně imperativního API Express. Nemusí to být triviální, ale lze to udělat.

Jako příklad zvažte tuto funkci, která bere tělo jako objekt k odeslání jako json:

const wrap = f => (req, res) => {
  const { status = 200, headers = {}, body = {} } = f(req);
  res.status(status).set(headers).json(body);
};

Dalo by se použít k vytvoření obslužných rutin tras, jako je tento:

app.get('/sum/:x/:y', wrap(req => ({
  headers: { 'Foo': 'Bar' },
  body: { result: +req.params.x + +req.params.y },
})));

pomocí funkce, která vrací jeden výraz bez vedlejších účinků.

Úplný příklad:

const app = require('express')();

const wrap = f => (req, res) => {
  const { status = 200, headers = {}, body = {} } = f(req);
  res.status(status).set(headers).json(body);
};

app.get('/sum/:x/:y', wrap(req => ({
  headers: { 'Foo': 'Bar' },
  body: { result: +req.params.x + +req.params.y },
})));

app.listen(4444);

Testování odpovědi:

$ curl localhost:4444/sum/2/4 -v
* Hostname was NOT found in DNS cache
*   Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 4444 (#0)
> GET /sum/2/4 HTTP/1.1
> User-Agent: curl/7.35.0
> Host: localhost:4444
> Accept: */*
> 
< HTTP/1.1 200 OK
< X-Powered-By: Express
< Foo: Bar
< Content-Type: application/json; charset=utf-8
< Content-Length: 12
< ETag: W/"c-Up02vIPchuYz06aaEYNjufz5tpQ"
< Date: Wed, 19 Jul 2017 15:14:37 GMT
< Connection: keep-alive
< 
* Connection #0 to host localhost left intact
{"result":6}

Samozřejmě je to jen základní myšlenka. Můžete vytvořit wrap() funkce přijímá sliby pro návratovou hodnotu funkcí pro asynchronní operace, ale pravděpodobně to nebude tak bez vedlejších efektů:

const wrap = f => async (req, res) => {
  const { status = 200, headers = {}, body = {} } = await f(req);
  res.status(status).set(headers).json(body);
};

a handler:

const delay = (t, v) => new Promise(resolve => setTimeout(() => resolve(v), t));

app.get('/sum/:x/:y', wrap(req =>
  delay(1000, +req.params.x + +req.params.y).then(result => ({
    headers: { 'Foo': 'Bar' },
    body: { result },
  }))));

Použil jsem .then() místo async /await v samotném handleru, aby to vypadalo funkčněji, ale může být zapsáno jako:

app.get('/sum/:x/:y', wrap(async req => ({
  headers: { 'Foo': 'Bar' },
  body: { result: await delay(1000, +req.params.x + +req.params.y) },
})));

Mohlo by to být ještě univerzálnější, kdyby funkce, která je argumentem, wrap by to byl generátor, který by místo toho, aby poskytoval pouze sliby k vyřešení (jako to obvykle dělají koroutiny založené na generátoru), přinášel by buď sliby k vyřešení, nebo chucks to stream, s nějakým obalem pro rozlišení obou. Toto je pouze základní myšlenka, ale může být rozšířena mnohem dále.




  1. Import/export MongoDB pomocí Node.js

  2. mongoose najít všechny neodesílající zpětné volání

  3. Automatické mazání dokumentů z mongodb po zadané době

  4. ClusterControl – Pokročilá správa zálohování – MongoDB