ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

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

2023-07-20 15:05:20  阅读:143  来源: 互联网

标签:函数 交互 Node


异步函数概述

异步函数是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
来源:

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有