编程语言
首页 > 编程语言> > 如何使用 Node 中的异步函数与数据库交互.js

如何使用 Node 中的异步函数与数据库交互.js

作者:互联网

异步函数概述

异步函数是JavaScript的一个相对较新的功能(不是特定于Node.js)。对该功能的支持首先登陆 Node.js v7.6 通过对 V8 JavaScript 引擎的更新。由于异步函数严重依赖 Promises,我建议您在继续之前阅读上一篇文章。

我喜欢将异步函数视为两部分:异步和等待。让我们依次看一下每个部分。

async 

长期以来,我们已经能够使用函数语句(必须命名)或函数表达式(通常是匿名的)在 JavaScript 中创建函数。

2023 年微服务和容器化将面临什么

DZone 的 2022 年微服务和容器化趋势报告探讨了云架构和设计原则的交集,深入探讨了微服务编排技术、微服务设计模式、容器安全性、服务网格等!



1
function getNumber() { // Function statment

2
  return 42;

3
}

4

5
let logNumber = function() { // Function expression

6
  console.log(getNumber());

7
}

8

9
logNumber(); // 42

如果在 Node.js 中运行上述脚本,则应看到打印到控制台。42

JavaScript 现在有这些构造的异步对应项。将 new 关键字放在函数语句或表达式之前将返回 AsyncFunction(异步函数)对象。async

1
async function getNumber() { // Async function statment

2
  return 42;

3
}

4

5
let logNumber = async function() { // Async function expression

6
  console.log(getNumber());

7
}

8

9
logNumber(); // Promise { 42 }

在 Node.js 中运行此脚本应打印 。如您所见,当调用异步函数时,它们返回的是 promise 而不是返回的实际值!Promise { 42 }

为了使基于异步的脚本在功能上等同于第一个脚本,我们必须按如下方式重写它。

1
async function getNumber() { // Async function statment

2
  return 42;

3
}

4

5
let logNumber = async function() { // Async function expression

6
  getNumber() // returns a promise

7
    .then(function(value) {

8
      console.log(value);

9
    });

10
}

11

12
logNumber(); // 42

现在我们回到记录值。42

正如我们在上一篇文章中看到的 promise 链接一样,如果 async 函数完成而没有错误,那么它返回的 promise 就会被解析。如果函数返回一个值,则它将成为承诺的值。如果抛出错误并且未处理,则承诺将被拒绝,错误将成为承诺的值。

虽然有趣,但返回承诺并不是使异步函数特别的原因。毕竟,我们可以从常规功能中返回承诺。使异步函数特别的是。await

await 

运算符仅在异步函数中可用,是魔术发生的地方。这就像在代码上按下暂停按钮,以便它可以等待承诺被解决或拒绝,然后再继续。这是一个称为协程的概念。自从引入生成器函数以来,协程在 JavaScript 中已经可用,但异步函数使它们更容易接近。await

等待不会阻塞主线程。相反,允许当前运行的调用堆栈(直到 为止)完成,以便可以执行回调队列中的其他函数。当承诺被解析或拒绝时,代码的剩余部分将排队等待执行。如果承诺已解析,则返回其值。如果承诺被拒绝,则在主线程上抛出被拒绝的值。await

下面是用于模拟异步 API 的演示。我添加了一些额外的控制台输出来帮助说明正在发生的情况。awaitsetTimeout

1
function getRandomNumber() {

2
  return new Promise(function(resolve, reject) {

3
    setTimeout(function() {

4
      const randomValue = Math.random();

5
      const error = randomValue > .8 ? true : false;

6

7
      if (error) {

8
        reject(new Error('Ooops, something broke!'));

9
      } else {

10
        resolve(randomValue);

11
      }

12
    }, 2000);

13
  }); 

14
}

15

16
async function logNumber() {

17
  let number;

18

19
  console.log('before await', number);

20

21
  number = await getRandomNumber();

22

23
  console.log('after await', number);

24
}

25

26
console.log('before async call');

27

28
logNumber();

29

30
console.log('after async call');

当此脚本在 Node 中运行时.js没有发生错误,输出将如下所示(我在发生两秒延迟的地方添加了一条注释)。

1
before async call

2
before await undefined

3
after async call

4
# 2 second delay

5
after await 0.22454453163016597

请注意,之前在等待 0.22454453163016597 之后记录。只有异步函数中的剩余代码被暂停;调用堆栈中的其余同步代码将完成。after async call

如果抛出错误,您将看到我们在上一篇文章中介绍的内容。拒绝可以使用该帖子中提到的方法或使用!UnhandledPromiseRejectionWarningtry…catch

尝试。。。抓住

在本系列的第一篇文章中,我解释了为什么块不适用于异步操作 - 您无法捕获当前调用堆栈之外发生的错误。但是现在我们有异步函数, 可以用于异步操作!try…catchtry…catch

下面是上一个脚本的精简版本,用于捕获异步 API 中发生的错误并改用默认值。

1
function getRandomNumber() {

2
  return new Promise(function(resolve, reject) {

3
    setTimeout(function() {

4
      const randomValue = Math.random();

5
      const error = randomValue > .8 ? true : false;

6

7
      if (error) {

8
        reject(new Error('Ooops, something broke!'));

9
      } else {

10
        resolve(randomValue);

11
      }

12
    }, 2000);

13
  }); 

14
}

15

16
async function logNumber() {

17
  let number;

18

19
  try {

20
    number = await getRandomNumber();

21
  } catch (err) {

22
    number = 42;

23
  }

24

25
  console.log(number);

26
}

27

28
logNumber();

如果运行该脚本的次数足够多,则最终将进入输出。 又工作了,呜呼!42try…catch

异步循环

除了能够再次使用块之外,我们还可以进行异步循环!在下面的示例中,我使用一个简单的循环,该循环按顺序记录三个值。try…catchfor

1
function getRandomNumber() {

2
  return new Promise(function(resolve, reject) {

3
    setTimeout(function() {

4
      const randomValue = Math.random();

5
      const error = randomValue > .8 ? true : false;

6

7
      if (error) {

8
        reject(new Error('Ooops, something broke!'));

9
      } else {

10
        resolve(randomValue);

11
      }

12
    }, 2000);

13
  }); 

14
}

15

16
async function logNumbers() {

17
  for (let x = 0; x < 3; x += 1) {

18
    console.log(await getRandomNumber());

19
  }

20
}

21

22
logNumbers();

在 Node.js 中运行此脚本,您应该看到每两秒打印到控制台的三个数字。没有第三方库,没有复杂的承诺链,只有一个简单的循环。循环再次工作,耶!

并行执行

显然,异步函数可以轻松执行顺序流,并将标准 JavaScript 构造与异步操作一起使用。但是平行流呢?这就是派上用场的地方。因为它们都返回 promise, 所以可以像任何其他基于 promise 的 API 一样使用它们。Promise.allPromise.raceawait

下面是一个使用 Promise.all 并行获取三个随机数的示例。

1
function getRandomNumber() {

2
  return new Promise(function(resolve, reject) {

3
    setTimeout(function() {

4
      const randomValue = Math.random();

5
      const error = randomValue > .8 ? true : false;

6

7
      if (error) {

8
        reject(new Error('Ooops, something broke!'));

9
      } else {

10
        resolve(randomValue);

11
      }

12
    }, 2000);

13
  }); 

14
}

15

16
async function logNumbers() {

17
  let promises = [];

18

19
  promises[0] = getRandomNumber();

20
  promises[1] = getRandomNumber();

21
  promises[2] = getRandomNumber();

22

23
  Promise.all(promises)

24
    .then(function(values) {

25
      console.log(values);

26
    })

27
    .catch(function(err) {

28
      console.log(err);

29
    });

30
}

31

32
logNumbers();

因为如果传入的任何承诺被拒绝,就会拒绝其承诺,因此您可能需要运行脚本几次才能看到打印出的三个随机数。Promise.all

异步函数演示应用

异步函数演示应用由以下四个文件组成。这些文件也可以通过此 Gist 获得。

package.json:

1
{

2
  "name": "async-functions",

3
  "version": "1.0.0",

4
  "description": "",

5
  "main": "index.js",

6
  "scripts": {

7
    "test": "echo \"Error: no test specified\" && exit 1"

8
  },

9
  "keywords": [],

10
  "author": "Dan McGhan <dan.mcghan@oracle.com> (https://jsao.io/)",

11
  "license": "ISC",

12
  "dependencies": {

13
    "oracledb": "^1.13.1"

14
  }

15
}

这是一个非常基本的文件。唯一的外部依赖项是oracledb。package.json

index.js:

1
const oracledb = require('oracledb');        

2
const dbConfig = require('./db-config.js');      

3
const employees = require('./employees.js');

4

5
async function startApp() {

6
  try {

7
    await oracledb.createPool(dbConfig);

8

9
    let emp = await employees.getEmployee(101);

10

11
    console.log(emp);

12
  } catch (err) {

13
    console.log('Opps, an error occurred', err);

14
  }

15
}

16

17
startApp();

node-oracledb 中的所有异步方法都被重载以使用回调函数或承诺。如果回调函数未作为最后一个参数传入,则将返回一个承诺。此版本的 使用带有驱动程序承诺 API 的运算符来创建连接池并提取员工。尽管池是从对 的调用返回的,但此处未引用该池,因为内置池缓存将在 中使用。index.jsawaitcreatePoolemployees.js

db-config.js:

1
module.exports = {

2
  user: 'hr',

3
  password: 'oracle',

4
  connectString: 'localhost:1521/orcl',

5
  poolMax: 20,

6
  poolMin: 20,

7
  poolIncrement: 0

8
};

该文件用于提供数据库的连接信息。此配置应适用于数据库应用开发 VM,但需要针对其他环境进行调整。db-config.jsindex.js

employees.js:

1
const oracledb = require('oracledb');

2

3
function getEmployee(empId) {

4
  return new Promise(async function(resolve, reject) {

5
    let conn; // Declared here for scoping purposes.

6

7
    try {

8
      conn = await oracledb.getConnection();

9

10
      console.log('Connected to database');

11

12
      let result = await conn.execute(

13
        `select *

14
        from employees

15
        where employee_id = :emp_id`,

16
        [empId],

17
        {

18
          outFormat: oracledb.OBJECT

19
        }

20
      );

21

22
      console.log('Query executed');

23

24
      resolve(result.rows[0]);

25
    } catch (err) {

26
      console.log('Error occurred', err);

27

28
      reject(err);

29
    } finally {

30
      // If conn assignment worked, need to close.

31
      if (conn) {

32
        try {

33
          await conn.close();

34

35
          console.log('Connection closed');

36
        } catch (err) {

37
          console.log('Error closing connection', err);

38
        }

39
      }

40
    }

41
  });

42
}

43

44
module.exports.getEmployee = getEmployee;

这个版本的 employee 模块类似于 promise 版本,因为该函数是作为基于 promise 的 API 编写的——它会立即返回一个异步解析或拒绝的新 promise 实例。主要区别在于,与驱动程序的承诺 API 一起使用,以获取与数据库的连接,使用它执行查询,然后关闭连接。getEmployeeawait

一个块用于捕获错误并确保连接以任何一种方式关闭。对我来说,这个版本的模块是该系列中所有模块中最容易阅读的,而且它的代码行数也最少也没有什么坏处。try…catch…finally

标签:函数,交互,Node
来源: