长答案短:它返回一个Promise<void>
回调
要了解原因,这里有一些细节。
首先必须了解 node.js 中的回调。回调是 node.js 工作方式的基本结构/功能之一。
你可以说 node.js 基本上是一个事件驱动的编程“框架”(大多数人会对框架这个词皱眉......)。这意味着您告诉节点,如果发生某件事,它应该执行某个操作/功能(回调)。
为了让节点理解我们,我们通常将回调函数作为参数提供给另一个函数,该函数将完成“监听事件”的工作并执行我们给它的回调。所以执行回调的不是“我们”,而是事件监听器。
在你的情况下,
userSchema.pre('save', async function (next) {
pre 是函数(Mongoose 的 userSchema 中的一个方法),save 是必须响应的事件,async function (next) { 是回调或事件后必须做的事情。
你会注意到你的回调返回next(),但next()返回void,这意味着你的回调返回void。
那么为什么返回Promise<void>?
事实上,在您的情况下,您的回调是一个异步函数。每个异步函数都会返回一个 Promise。它是一个异步函数,因为它正在等待它内部的另一个承诺(甚至两个承诺)。由于await,它们被隐藏了
const salt = await bcrypt.genSalt(config.get<number>('saltWorkFactor'));
const hash = await bcrypt.hash(user.password, salt);
注意:bcrypt 方法在 CPU 和时间方面非常昂贵(这也是一项安全功能)。
这也意味着通常在您的代码中
const hash = await bcrypt.hash(user.password, salt);
user.password = hash;
您无法“立即”获得user.password 的hash 值,更糟糕的是,您甚至不知道它何时会到来。您的程序会停止并等到bcrypt 完成其业务吗?
如果你有很多 async 函数,那么你的程序将是奥运会上最慢的冠军的最爱。
这些承诺是怎么回事?我们怎么能不被贴上老年项目的标签?
承诺
这是一个快速/长评论,试图解释承诺的概念。
在“正常”代码中,每一行代码都会在下一行之前执行并“完成”。例如:(带烹饪)
或者在你的代码中:
let user = this as UserDocument;
if(!user.isModified('password')){
return next();
}
promise 是在下一行代码之前执行但尚未完成的特定代码。例如:
- 当蛋糕在烤箱里时(承诺),
- 你准备糖霜,
- 但您不能在蛋糕烤好之前将其放入(promise 的“then”操作)。
注意:您的代码使用的是await,因此没有“显式”then 方法。
你会在日常生活中有很多“承诺”的例子。您可能听说过异步代码 = 不是一个接一个,不同步,...
- 早上打开闹钟叫醒你,
then 你保证不会忽视它;
- 在日历上提醒
then,您承诺您将参加那次工作面试;等
在做出这些承诺之后,你一直在继续你的生活。
在代码中,返回 promise 的函数将具有 then 方法,您可以在该方法中告诉计算机在“警报响起”时该做什么。
一般是这样写的
mypromise().then(doThisThingFunction)
const continueWithMyLife = true
这样then方法就和node.js的回调很像了。它只是在代码中以不同的方式表达,并不特定于节点(回调也不特定于节点......)。
它们之间的一个非常重要的区别是,回调是侦听器“做”的事情,而承诺是(希望)解析为返回值的事情。
异步/等待
现在使用async/await 很常见。幸运/不幸的是,它基本上隐藏了异步行为。更好地阅读代码,但对于新程序员来说,对 Promise 的理解也更差。
在await 之后,没有then 方法(或者您可以说以下代码行是then 操作)。没有“继续你的生活”。只有“等到警报响起”,所以await之后的下一行本质上是“下床动作”。
这就是为什么在您的代码中,hash 值在下一行中可用。基本上是用“旧方式”来写承诺
user.password = hash;
将在 then 函数内。
这也是它返回Promise<void>的原因
但是,所有这些类比都无济于事。最好的办法是在日常代码中尝试一下。没有什么比经验更能理解任何事情。