理解带有 promises 和 undefined var 的奇怪错误
Understanding weird bug with promises and undefined var
我刚刚发现当几乎同时触发 2 个请求时,我的 API 会做一些奇怪的事情。
我发现问题是我在下面的 "user" 变量之前缺少 "var" 声明,但我真的很好奇导致下面描述的错误的根本问题:
我有两个 API 端点调用相同的函数如下:
router.get('/refresh_session_token', function (req, res) {
let user_id = req.body.user_id // The value sent is 8
findUserWithId(user_id)
.then(user_data => {
user = user_data // I forgot 'var' here
})
.then(() => {
console.log(user) // This should always show user data from user_id = 8
})
}
router.get('/resend_invite', function (req, res) {
let user_id = req.body.user_id // The value sent is 18
findUserWithId(user_id)
.then(user_data => {
user = user_data // I forgot 'var' here
})
.then(() => {
console.log(user) // This should always show user data from user_id = 18
})
}
const findUserWithId = (id) => {
return knex.raw(`SELECT * FROM users WHERE id = ?`, [id]).then((data) => data.rows[0])
}
所有这些代码都在我通过 module.exports = router;
导出的同一个文件中
我发现,如果我几乎在触发端点 /refresh_session_token 和 /resend_invite同时每个都有两个不同的 user_id,有时候,我的 console.log returns 两者的结果相同,就好像我使用相同的 user_id.
向用户添加 var 解决了这个问题,但我对后台实际发生的事情感到非常惊讶。
你有什么想法吗?
当你没有声明你的变量并且你没有在 Javascript 的 strict
模式下 运行 设置你的模块时,那么对该变量的第一个赋值是:
user = user_data
创建一个名为 user
的自动全局变量。这意味着您的两条路线将共享同一个变量。
而且,由于您的两条路线都在其中进行了异步操作,即使是单线程事物,您的两条路线仍然可以同时运行,并且都尝试使用相同的全局变量。一条路线将覆盖另一条路线的值。这在基于服务器的代码中是一场灾难,因为通常情况下,错误不会在您投入生产之前显示出来,而且很难找到可重现的案例。
这里最好的答案是总是运行你的代码处于严格模式,然后 JS 解释器会报错,你永远不会被允许 运行 你的代码在第一名。错误会很快很容易地被发现。
那么显然,总是用 let
或 const
声明变量。很少有理由再使用 var
,因为 let
和 const
可以让您更好地控制变量的范围。
在严格模式下运行你的模块,插入:
'use strict';
在任何其他 Javascript 语句之前。
或者,使用像 TypeScript 这样的东西,它不会让你做一些草率的事情,比如不声明你的变量。
我刚刚发现当几乎同时触发 2 个请求时,我的 API 会做一些奇怪的事情。
我发现问题是我在下面的 "user" 变量之前缺少 "var" 声明,但我真的很好奇导致下面描述的错误的根本问题:
我有两个 API 端点调用相同的函数如下:
router.get('/refresh_session_token', function (req, res) {
let user_id = req.body.user_id // The value sent is 8
findUserWithId(user_id)
.then(user_data => {
user = user_data // I forgot 'var' here
})
.then(() => {
console.log(user) // This should always show user data from user_id = 8
})
}
router.get('/resend_invite', function (req, res) {
let user_id = req.body.user_id // The value sent is 18
findUserWithId(user_id)
.then(user_data => {
user = user_data // I forgot 'var' here
})
.then(() => {
console.log(user) // This should always show user data from user_id = 18
})
}
const findUserWithId = (id) => {
return knex.raw(`SELECT * FROM users WHERE id = ?`, [id]).then((data) => data.rows[0])
}
所有这些代码都在我通过 module.exports = router;
导出的同一个文件中我发现,如果我几乎在触发端点 /refresh_session_token 和 /resend_invite同时每个都有两个不同的 user_id,有时候,我的 console.log returns 两者的结果相同,就好像我使用相同的 user_id.
向用户添加 var 解决了这个问题,但我对后台实际发生的事情感到非常惊讶。
你有什么想法吗?
当你没有声明你的变量并且你没有在 Javascript 的 strict
模式下 运行 设置你的模块时,那么对该变量的第一个赋值是:
user = user_data
创建一个名为 user
的自动全局变量。这意味着您的两条路线将共享同一个变量。
而且,由于您的两条路线都在其中进行了异步操作,即使是单线程事物,您的两条路线仍然可以同时运行,并且都尝试使用相同的全局变量。一条路线将覆盖另一条路线的值。这在基于服务器的代码中是一场灾难,因为通常情况下,错误不会在您投入生产之前显示出来,而且很难找到可重现的案例。
这里最好的答案是总是运行你的代码处于严格模式,然后 JS 解释器会报错,你永远不会被允许 运行 你的代码在第一名。错误会很快很容易地被发现。
那么显然,总是用 let
或 const
声明变量。很少有理由再使用 var
,因为 let
和 const
可以让您更好地控制变量的范围。
在严格模式下运行你的模块,插入:
'use strict';
在任何其他 Javascript 语句之前。
或者,使用像 TypeScript 这样的东西,它不会让你做一些草率的事情,比如不声明你的变量。