前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >node.js异步请求大坑

node.js异步请求大坑

作者头像
SimpleAstronaut
发布2022-09-21 10:11:08
2.2K0
发布2022-09-21 10:11:08
举报
文章被收录于专栏:LMC的摸鱼博客

前段时间写Node.js执行mysql的时候踩了个大坑,大概就是nodejs请求Mysql数据表中的数据,返回以后,如果匹配正确就向另一个数据表中写数据。

Node.js express框架的一个get请求接口,具体操作是从数据库中检索验证码,如果正确就往另一个数据表中写入数据 原始代码:

代码语言:javascript
复制
app.get('/mailconfirm', function (req, res) {
    const mail = req.query.mail;
    const cap = req.query.cap;

    var findmailsql = "SELECT * FROM CAP WHERE MAIL='" + mail + "'";
    connection.query(findmailsql, function (err, result) {
        if (err) {
            console.log('[INSERT ERROR] - ', err.message);
            return;
        }
        if (result == 0) {
            res.send(eval('(' + '{"status":300,"msg":"NO EMAIL"}' + ')'));
        } else {
            let FindCapSql = "SELECT * FROM `CAP` WHERE MAIL='"+mail+"' AND CAP="+cap;
            //= eval('(' + '{"status":300,"msg":"CAP FALSE"}' + ')');

            connection.query(FindCapSql, function(err, result){
                if (err) {
                    console.log('[INSERT ERROR] - ', err.message);
                    return;
                }
                if(result == 0){
                    res.send(eval('(' + '{"status":300,"msg":"CAP FALSE"}' + ')'));
                } else {
                    console.log(result);
                    let insertEmailSql = 'UPDATE USERS SET MAIL = ? WHERE USER_ID = ?';
                    let insertEmailSqlValue = [mail, result.USER_ID];
                    connection.query(insertEmailSql, insertEmailSqlValue, function (err, result) {
                        if (err) {
                            console.log('[UPDATE ERROR] - ', err.message);
                            return;
                        }
                        console.log('test');
                        ret = result;
                        res.send(ret);
                        return;
                    })
                }
            })

            /*
            for (let i = 0; i < result.length; i++) {
                if (result[i].CAP == cap) {
                    let insertEmailSql = 'UPDATE USERS SET MAIL = ? WHERE USER_ID = ?';
                    let insertEmailSqlValue = [mail, result[i].USER_ID];
                    connection.query(insertEmailSql, insertEmailSqlValue, function (err, result) {
                        if (err) {
                            console.log('[UPDATE ERROR] - ', err.message);
                            return;
                        }
                        console.log('test');
                        ret = result;
                        res.send(ret);
                        return;
                    })
                }
            }
            console.log('test2');
            //console.log(result.length);*/
        }
        //res.send(ret);
    })
})

上面这段是修改后的代码,问题复现的结构大致如下:

代码语言:javascript
复制
for(let i=0; i<10; i++){
    if(result[i] === 'test'){
        connection.query(sql, function(err, result){
            console.log('1');
        })
    }
}
console.log('2');

上述代码运行以后在进入for以后,由于mysql请求是异步请求,执行的时候控制台输出’2’会比mysql请求后输出‘1’提前执行,控制台会先输出2再输出1。

这里我想到是用Promise重新将接口进行包装,使得可以使用async/await进行调用,符合同步的编码习惯

1.promise封装接口

Promise 通常被定义为最终会变为可用值的代理

Promise 是一种处理异步代码(而不会陷入回调地狱)的方式。

多年来,promise 已成为语言的一部分(在 ES2015 中进行了标准化和引入),并且最近变得更加集成,在 ES2017 中具有了 async 和 await

异步函数 在底层使用了 promise,因此了解 promise 的工作方式是了解 async 和 await 的基础。

1.1 Promise 如何运作

当 promise 被调用后,它会以处理中状态开始。 这意味着调用的函数会继续执行,而 promise 仍处于处理中直到解决为止,从而为调用的函数提供所请求的任何数据。

被创建的 promise 最终会以被解决状态被拒绝状态结束,并在完成时调用相应的回调函数(传给 then 和 catch)。

1.3 Promise

Promise API 公开了一个 Promise 构造函数,可以使用 new Promise() 对其进行初始化:

代码语言:javascript
复制
let done = true

const isItDoneYet = new Promise((resolve, reject) => {
  if (done) {
    const workDone = '这是创建的东西'
    resolve(workDone)
  } else {
    const why = '仍然在处理其他事情'
    reject(why)
  }
})

如你所见,promise 检查了 done 全局常量,如果为真,则 promise 进入被解决状态(因为调用了 resolve 回调);否则,则执行 reject 回调(将 promise 置于被拒绝状态)。 如果在执行路径中从未调用过这些函数之一,则 promise 会保持处理中状态。

使用 resolve 和 reject,可以向调用者传达最终的 promise 状态以及该如何处理。 在上述示例中,只返回了一个字符串,但是它可以是一个对象,也可以为 null。 由于已经在上述的代码片段中创建了 promise,因此它已经开始执行。 这对了解下面的消费 promise 章节很重要。

一个更常见的示例是一种被称为 Promisifying 的技术。 这项技术能够使用经典的 JavaScript 函数来接受回调并使其返回 promise:

代码语言:javascript
复制
const fs = require('fs')

const getFile = (fileName) => {
  return new Promise((resolve, reject) => {
    fs.readFile(fileName, (err, data) => {
      if (err) {
        reject(err)  // 调用 `reject` 会导致 promise 失败,无论是否传入错误作为参数,
        return        // 且不再进行下去。
      }
      resolve(data)
    })
  })
}

getFile('/etc/passwd')
.then(data => console.log(data))
.catch(err => console.error(err))

现在,看看如何消费或使用 promise。

代码语言:javascript
复制
const isItDoneYet = new Promise(/* ... 如上所述 ... */)
//...

const checkIfItsDone = () => {
  isItDoneYet
    .then(ok => {
      console.log(ok)
    })
    .catch(err => {
      console.error(err)
    })
}

运行 checkIfItsDone() 会指定当 isItDoneYet promise 被解决(在 then 调用中)或被拒绝(在 catch 调用中)时执行的函数。

1.4 解决问题

使用链式promise处理两次异步mysql请求

基础的mysql异步调用如下:

代码语言:javascript
复制
function ControlAPI_obj(data, callback){
    var sqlObj = _structureAnalysis(data);
    dataBaseControl(sqlObj["sql"], sqlObj["value"], (result)=>{
        if (result == null || result.length == 0) {
            callback(null);
        } else {
            callback(result);
        }
    });
};

将上面的结构改写为同步调用:

代码语言:javascript
复制
// 传入单条SQL语句
var ControlAPI_obj_async = function(data) {
    var sqlObj = _structureAnalysis(data);
    return new Promise((resolved, rejected)=>{
        dataBaseControl(sqlObj["sql"], sqlObj["value"], (result)=>{
            if (result === null) {
                rejected(null);
            } else {
                resolved(result);
            }
        });
    });
}

// 传入多条SQL语句
var ControlAPI_objs_async = function(...vars) {
    let len = vars.length;
    let promiseList = [];
    for(let i = 0; i < len; i++){
        let sqlObj = _structureAnalysis(vars[i]);
        promiseList.push(new Promise((resolved, rejected)=>{
            dataBaseControl(sqlObj["sql"], sqlObj["value"], (result)=>{
                if (result === null) {
                    rejected(null);
                } else {
                    resolved(result);
                }
            });
        }));
    }
    return Promise.all(promiseList);

使用方法

代码语言:javascript
复制
// 同步写法
async function(...){
    [...]
    try {
        let result_single = await ControlAPI_obj_async(obj1);
        let result_multi = await ControlAPI_objs_async(obj1, obj2,...,objn);
    } catch (error) {
        // 捕获await中Promise的reject的数据
    }
    [...]
}
// 异步写法
function(...) {
    ControlAPI_obj(data, (result) => {
        // 对result进行操作
    })
}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022年8月9日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1.promise封装接口
    • 1.1 Promise 如何运作
      • 1.3 Promise
        • 1.4 解决问题
        相关产品与服务
        云数据库 SQL Server
        腾讯云数据库 SQL Server (TencentDB for SQL Server)是业界最常用的商用数据库之一,对基于 Windows 架构的应用程序具有完美的支持。TencentDB for SQL Server 拥有微软正版授权,可持续为用户提供最新的功能,避免未授权使用软件的风险。具有即开即用、稳定可靠、安全运行、弹性扩缩等特点。
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档