Skip to content

Instantly share code, notes, and snippets.

@lynsei
Last active February 4, 2022 00:37
Show Gist options
  • Save lynsei/b9c88ed6aaad35061c825f2488383f15 to your computer and use it in GitHub Desktop.
Save lynsei/b9c88ed6aaad35061c825f2488383f15 to your computer and use it in GitHub Desktop.
[Node.js Promise Best Practices with Node or Deno and Rust] #advice #promises #deno #nodejs #callback #promise #async #await

Best practices

When converting from promise-based to callback-based APIs, the most obvious way is like this:

promise.then((value) => callback(null, value)).catch(callback);

This has a subtle bug - if the callback throws an error, the catch statement will also catch that error, and the callback will be called twice. The correct way to do it is like this:

promise.then((value) => callback(null, value), callback);

The second parameter of then can also be used to catch errors, but only errors from the existing promise, not the new one created by the callback.

If the Deno equivalent is actually synchronous, there's a similar problem with try/catch statements:

try {
  const value = process();
  callback(null, value);
} catch (err) {
  callback(err);
}

Since the callback is called within the try block, any errors from it will be caught and call the callback again.

The correct way to do it is like this:

let err, value;
try {
  value = process();
} catch (e) {
  err = e;
}
if (err) {
  callback(err); // Make sure arguments.length === 1
} else {
  callback(null, value);
}

It's not as clean, but prevents the callback being called twice.

Std Deno Library

This library has node compatibility

https://deno.land/[email protected]/node

Crypto

Streaming Crypto

  const source = Readable.from(["abc", "def"]);
  const hash = createHash("sha1");
  const dest = source.pipe(hash);

  const result = await new Promise((resolve, _) => {
    let buffer = Buffer.from([])
    dest.on("data", (data) => {
      buffer = Buffer.concat([buffer, data]);
    })
    dest.on("end", () => {
      resolve(buffer)
    })
  })

	Buffer.from([
      0x1f,
      0x8a,
      0xc1,
      0xf,
      0x23,
      0xc5,
      0xb5,
      0xbc,
      0x11,
      0x67,
      0xbd,
      0xa8,
      0x4b,
      0x83,
      0x3e,
      0x5c,
      0x5,
      0x7a,
      0x77,
      0xd2,
    ])

  for (const algorithm of getHashes()) {
    const d = createHash(algorithm).update("abc").digest();
    assert(d instanceof Buffer);
    assert(d.length > 0);
  }


Curl Request as Deno code

// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
const url_ = Deno.args[0];
const res = await fetch(url_);

// TODO(ry) Re-enable streaming in this example.
// Originally we did: await Deno.copy(res.body, Deno.stdout);
// But maybe more JS-y would be: res.pipeTo(Deno.stdout);

const body = new Uint8Array(await res.arrayBuffer());
await Deno.stdout.write(body);
// these are just good deno examples of async
import { parse } from "../flags/mod.ts";
import { readStringDelim } from "../io/buffer.ts";
// See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/AsyncFunction.
const AsyncFunction = Object.getPrototypeOf(async function () {})
.constructor;
const HELP_MSG = `xeval
Run a script for each new-line or otherwise delimited chunk of standard input.
Print all the usernames in /etc/passwd:
cat /etc/passwd | deno run -A https://deno.land/std/examples/xeval.ts "a = $.split(':'); if (a) console.log(a[0])"
A complicated way to print the current git branch:
git branch | deno run -A https://deno.land/std/examples/xeval.ts -I 'line' "if (line.startsWith('*')) console.log(line.slice(2))"
Demonstrates breaking the input up by space delimiter instead of by lines:
cat LICENSE | deno run -A https://deno.land/std/examples/xeval.ts -d " " "if ($ === 'MIT') console.log('MIT licensed')",
USAGE:
deno run -A https://deno.land/std/examples/xeval.ts [OPTIONS] <code>
OPTIONS:
-d, --delim <delim> Set delimiter, defaults to newline
-I, --replvar <replvar> Set variable name to be used in eval, defaults to $
ARGS:
<code>`;
export type XevalFunc = (v: string) => void;
export interface XevalOptions {
delimiter?: string;
}
const DEFAULT_DELIMITER = "\n";
export async function xeval(
reader: Deno.Reader,
xevalFunc: XevalFunc,
{ delimiter = DEFAULT_DELIMITER }: XevalOptions = {},
) {
for await (const chunk of readStringDelim(reader, delimiter)) {
// Ignore empty chunks.
if (chunk.length > 0) {
await xevalFunc(chunk);
}
}
}
async function main() {
const parsedArgs = parse(Deno.args, {
boolean: ["help"],
string: ["delim", "replvar"],
alias: {
delim: ["d"],
replvar: ["I"],
help: ["h"],
},
default: {
delim: DEFAULT_DELIMITER,
replvar: "$",
},
});
if (parsedArgs._.length != 1) {
console.error(HELP_MSG);
console.log(parsedArgs._);
Deno.exit(1);
}
if (parsedArgs.help) {
return console.log(HELP_MSG);
}
const delimiter = parsedArgs.delim;
const replVar = parsedArgs.replvar;
const code = parsedArgs._[0];
// new AsyncFunction()'s error message for this particular case isn't great.
if (!replVar.match(/^[_$A-z][_$A-z0-9]*$/)) {
console.error(`Bad replvar identifier: "${replVar}"`);
Deno.exit(1);
}
const xEvalFunc = new AsyncFunction(replVar, code);
await xeval(Deno.stdin, xEvalFunc, { delimiter });
}
if (import.meta.main) {
main();
}

Deno is a JavaScript/TypeScript runtime with simple, modern, secure defaults and a great developer experience. It’s built on V8, Rust, and Tokio.

Features:

-  Secure by default. No file, network, or environment access (unless explicitly enabled).
- Supports TypeScript out of the box.
- Ships a single executable (deno).
- Has built-in utilities like a dependency inspector (deno info) and a code formatter (deno fmt).
- Has a set of reviewed (audited) standard modules that are guaranteed to work with Deno.
-Scripts can be bundled into a single JavaScript file.

Prerequisites:

- A Ubuntu 20.04 installed dedicated server or KVM VPS.
- A root user access or normal user with administrative privileges.

Install Deno on Ubuntu 20.04 Step 1 – Keep the server updated

    # apt update -y && apt upgrade -y

Step 2 – Install required package

We need to install unzip as it is required during the installation process.

    # apt install unzip -y

Step 3 – Install Deno

There is one-line command to install Deno on system.

    # curl -fsSL https://deno.land/x/install/install.sh | sh

Once the installation gets complete, run following command to move deno environment file to /usr/bin directory.

# mv /root/.deno/bin/deno /usr/bin/

Step 4 – Verify the installation

We can verify the installation by checking the version using following command:

    # deno –version

Output:

    deno 1.7.4 (release, x86_64-unknown-linux-gnu)
    v8 9.0.123
    typescript 4.1.4

Step 5 – Run simple command

Try running a simple program using following command:

    # deno run https://deno.land/std/examples/welcome.ts

Or a more complex one:

    import { serve } from “https://deno.land/[email protected]/http/server.ts”;

    const s = serve({ port: 8000 });
 
    console.log(“http://localhost:8000/”);

    for await (const req of s) {

    req.respond({ body: “Hello World\n” });

    }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment