# # introduction

To be honest, there are still a lot of front-end newcomers in 9012 who don’t understand Callback well enough, and it’s amazing how often they make silly mistakes when calling a Callback type SDK when using Koa or Egg.

In the process of answering questions in Egg, I have encountered similar problems many times, so I have no choice but to come up with this article.

But this article does not attempt to explain how they work, just a quick Case to show how to use them.


## A simple scenario

Consider this scenario:




## Express implementation

const fs = require('fs');
const path = require('path');
const express = require('express');
const app = express(a);

app.use((req. res. next) = > {
  const filePath = path.join(__dirname. 'index.html');

  // Read the file  fs.readFile(filePath. (err. content) = > {
    if (err) {
      // Error reading file      return next(err);
    } else {
      // The file was read successfully      res.status(500);
      res.type('html');
      res.end(content.toString());
    }
  });
});

app.listen(3000. (a) = > console.log('Server running at http://127.0.0.1:3000/));
Copy the code

As above, calling an SDK with Callback from middleware requires handling success and failure separately in the Callback.

The middleware model of Express does not proceed and respond to the user without actively calling next(err) or res.end(). So it can be handled directly inside the callback.


Error implementation of ## Koa

If you follow suit, do the following:

const fs = require('fs');
const path = require('path');
const Koa = require('koa');
const app = new Koa(a);

app.use(async (ctx. next) = > {
  const filePath = path.join(__dirname. 'index.html');

  // Read the file  fs.readFile(filePath. (err. content) = > {
    ctx.type = 'html';
    ctx.status = 200;
    ctx.body = content.toString(a);
  });
});

app.listen(3000. (a) = > console.log('Server running at http://127.0.0.1:3000/));
Copy the code

You may find that the client receives a 404 response before the file has even been read.

Because in Koa, the Onion model will be executed one after another, and if you do not control the execution timing with await Promise, it will automatically respond to the user. If ctx.body is empty, the response will be 404.


## Correct implementation of Koa

const fs = require('fs');
const path = require('path');
const util = require('util');
const Koa = require('koa');
const app = new Koa(a);

// Encapsulate fs.readFile as a Promiseconst readFile = util.promisify(fs.readFile);

app.use(async (ctx. next) = > {
  const filePath = path.join(__dirname. 'index.html');

  // Read the file in await mode  const result = await readFile(filePath);

  ctx.type = 'html';
  ctx.status = 200;
  ctx.body = result;
});

app.listen(3000. (a) = > console.log('Server running at http://127.0.0.1:3000/));
Copy the code

As mentioned above, all of the SDK for Callback needs to be converted to the form of Promise before it can be used in Koa.


## Promisify

As mentioned above, util.promisify(fs.readfile) is a syntaxal sugar for Node.js 8.x, which is equivalent to the following encapsulation:

// Encapsulate fs.readFile as a Promiseasync function readFile(filePath) {
  return new Promise((resolve. reject) = > {
    fs.readFile(filePath. (err. content) = > {
      // Call reject on a read error      if (err) return reject(err);

      // Call resolve if the read succeeds      return resolve(content.toString());
    });
  });
}
Copy the code

We can also similarly encapsulate setTimeout as a sleep:

/ / defineasync function sleep(ms) {
 return new Promise(resolve = > {
   setTimeout(resolve. ms);
 });
}

/ / use:await sleep(1000);
Copy the code

If you’re using an SDK that only provides Callback, you can similarly wrap it and use it.

Of course, for the most part, we just use util.promisify or some encapsulated class, such as Egg:

  • https://www.npmjs.com/package/mz
  • https://www.npmjs.com/package/mz-modules
const { mkdirp. rimraf. sleep } = require('mz-modules');
const { fs } = require('mz');

async function run(a) {
  // Delete directories in non-blocking mode  await rimraf('/path/to/dir');

  // +1s
  await sleep('1s');

  // non-blocking mkdir -p  await mkdirp('/path/to/dir');

  // Read the file, please put 'fs.readfilesync' out of your mind completely.  const content = await fs.readFile('/path/to/file.md'. 'utf-8');
}
Copy the code


## Related information

For those of you who are not familiar with Promise and Async in 2019, let’s go back and learn:

  • http://es6.ruanyifeng.com/#docs/promise
  • http://es6.ruanyifeng.com/#docs/async
  • https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Promise
  • https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Statements/async_function
  • https://github.com/sindresorhus/promise-fun
  • And Koa’s onion model.