初识Promise踩坑记 ——青训营笔记

86 阅读3分钟

这是我参与「第四届青训营 」笔记创作活动的第2天

我们团队选择了“仿掘金官网”的题目作为大项目。

在前期的开发中,我们使用的Java的SpringMVC来做后端,但由于我们团队只有负责后端的那一个同学会Java,所以开发,调试,排错,以及最后与我这个“运维”对接上线都存在很多不便的地方。所以我就决心参照原本的Java实现,用nodejs重写一遍项目后端。

需求提出

写后端的细节不表,开发完成后,负责前端的同学提了个需求,就是能否在返回的文章列表里面带上作者名字?

这里不得不说我们的数据库的结构了,数据库是有两个表,分别是文章表和用户表,每一篇文章里都有用户的ID,用户的ID可在用户表里找到用户的名称。

最初该接口的实现是根据id直接去文章表里请求就行了,返回值就是请求的结果。

router.get('/queryAllArticle', function (req, res, next) {
    query('SELECT id,title,intro,time,author,tag,visit FROM article ORDER BY RAND() LIMIT 10', [], (err, result) => {
        if (err) {
            res.status(500);
            res.render('error');
        }
        else { res.send(result); }
    });
});

需求实现

其实现在回过头来看,要实现这个需求只需要在SQL语句上做小小的改动就行了。只需改成:

SELECT a.id,a.title,a.intro,a.time,a.author,a.tag,a.visit,p.nickname FROM article a,people p where a.author=p.id;

可当时我连着开发了几天的后端,头也昏昏沉沉的,连数据库的基本操作都忘了,所以我想出来的解决方法是,根据每一个出来的作者id,再在people表中查到作者名字,再添加到结果中返回。

于是,初步的代码是这样:

result.forEach(function (val, index) {
        query('SELECT nickname FROM people where id = ?', [val.author], (err, result) => {
            if (err) { res.status(500); res.send('error'); }
            else {
                //将结果的nickname加入result中
            }
        })
    })
}
res.send(result)

咋一看没有任何问题,但是,这个query是个异步函数!也就是可能第一个结果还没出来,下面的send就执行了,于是什么也没有发生。

于是,我们的主角,Promise,出场了。

ES6 中的 Promise 是异步编程的一种方案。从语法上讲,Promise 是一个对象,它可以获取异步操作的消息。

但是,要怎样才能确保每一个操作都能完成呢?根据resolve的特性,我们可以通过设置flag的方式来实现。

最终代码如下:

let AddAuthorName = (result) => {
    return new Promise((resolve, reject) => {
        if (!result.length) { resolve(result); }
        let flag = result.length;
        result.forEach(function (val, index) {
            query('SELECT nickname FROM people where id = ?', [val.author], (err, res) => {
                if (err) { reject(err) }
                else {
                    val.authorName = res[0].nickname;
                    result[index] == val;
                    flag--;
                    if (!flag) {
                        resolve(result);
                    }
                }
            })
        })
    })
}

router.get('/queryAllArticle', function (req, res, next) {
    query('SELECT id,title,intro,time,author,tag,visit FROM article ORDER BY RAND() LIMIT 10', [], (err, result) => {
        if (err) {
            res.status(500);
            res.send('error');
        }
        else {
            AddAuthorName(result).then((val) => { res.send(val); }, (err) => { res.status(500); res.send('error')})
        }
    });
});

后记

其实写完了才发现,本来这篇文章是要写Promise的,结果写到一半才发现在数据库上可以有更优解。或许这就是写笔记的意义吧,在回顾过去的知识的同时迸发新的思考。