當我將滑鼠懸停在關鍵字“功能”上時,描述會顯示:
"(本地函式)(this: any, next: (err?: mongoose.CallbackError | undefined) => void): Promise<void>"
那么它回傳 aPromise<void>還是 simple <void>?我什至無法理解這個函式回傳什么?老實說,我不太了解Promise<void>...的概念
userSchema.pre('save', async function (next) {
let user = this as UserDocument;
if(!user.isModified('password')){
return next();
}
const salt = await bcrypt.genSalt(config.get<number>('saltWorkFactor'));
const hash = await bcrypt.hash(user.password, salt);
user.password = hash;
return next();
})
uj5u.com熱心網友回復:
這個問題真的很有趣。您的函式回傳 a ,它與預期的回傳型別Promise<void>兼容,但 Mongoose 非常聰明,足以知道如何處理您的 Promise,因此您甚至根本不需要呼叫。voidprenext
首先是一些背景:
void在 TypeScript 中具有特殊含義,表示回傳值可以是任何值;該值經常出現undefined(因為這是一個函式在沒有return陳述句的情況下回傳的值),但它不一定是。正如在 TypeScript FAQ中一樣,這使得通過指示回傳值未使用來接受或傳遞回傳值的函式變得很方便。如果您需要提供一個回傳型別為 的函式void,您可以傳回一個回傳 astring、Promise<void>、Promise<SomeObject>、null、undefined或其他任何內容的函式。- 所有
async函式都回傳 Promises,這也不例外。APromise<number>是一個 Promise,表示它的then函式將收到一個number; a是一個 Promise,它不會告訴你它的函式接收Promise<void>到什么。then該then函式仍將被呼叫,除非它有一個錯誤catch;你只是不太了解它的論點。 - 在 Mongoose 的型別中,
pre接受一個PreSaveMiddlewareFunction<T>函式,即您撰寫的函式的型別。它接受一個名為next并回傳的函式void:Mongoose聲稱不關心您回傳的內容。你的中間件函式可以是異步的;完成后,您應該呼叫next(帶有錯誤物件,如果有的話),并且該呼叫next也回傳void.
您傳遞給pre回傳型別Promise<void>的函式:該函式async絕對回傳一個承諾,并且您的return next();意思是承諾決議為任何next回傳,定義為void. 你不知道next回傳什么,也不應該關心它。你甚至不需要return next(),你只需要呼叫它:它只是一個回呼,所以你可以告訴 Mongoose 你的中間件已經完成并報告任何錯誤。
所以你的 return ,但這適用于:的定義,不關心你的函式有什么樣的回傳值 ( async function)只要你打電話來表明你已經完成了。Promise<void>preprevoidnext
可是等等!報告您的異步函式已完成以及是否有錯誤正是Promises 旨在解決的問題,而next回呼模式正是 Promises 旨在取代的那種模式。next如果你要回傳一個 Promise,當 Mongoose 可以看到你回傳的 Promise 時,你為什么還要打電話呢?
事實上,在 Mongoose 5.x 或更高版本中,這正是發生的情況:如果您傳入的函式pre回傳一個 Promise,那么您可以使用它而不是呼叫next. next為了兼容性,您仍然可以手動呼叫,但在您的情況下,您可以洗掉return next()并且一切都會繼續作業。請參閱中間件檔案:
在mongoose 5.x
next()中,您可以使用回傳承諾的函式,而不是手動呼叫。特別是,您可以使用async/await.schema.pre('save', function() { return doStuff(). then(() => doMoreStuff()); }); // Or, in Node.js >= 7.6.0: schema.pre('save', async function() { await doStuff(); await doMoreStuff(); });
檔案進一步解釋了為什么return next()是一種模式:
如果使用
next(),則next()呼叫不會停止中間件函式中的其余代碼執行。使用earlyreturn模式可以防止在呼叫next().const schema = new Schema(..); schema.pre('save', function(next) { if (foo()) { console.log('calling next!'); // `return next();` will make sure the rest of this function doesn't run /*return*/ next(); } // Unless you comment out the `return` above, 'after next' will print console.log('after next'); });
總而言之,預期的回傳型別void與您回傳 a 的事實兼容Promise<void>,但它隱藏了這樣一個事實,即最近版本的 Mongoose 足夠聰明,可以檢查您是否回傳 Promise 并在不需要 a 的情況下做正確的事情打電話給next. 它們是兩種不同的風格,兩者都有效。
uj5u.com熱心網友回復:
長答案短:它回傳一個Promise<void>
回呼
要了解原因,這里有一些細節。首先必須了解 node.js 中的回呼。回呼是 node.js 作業方式的基本結構/功能之一。
你可以說 node.js 基本上是一個事件驅動的編程“框架”(大多數人會對框架這個詞皺眉......)。這意味著您告訴節點,如果發生某件事,它應該執行某個操作/功能(回呼)。
為了讓 node 理解我們,我們通常將回呼函式作為引數提供給另一個函式,該函式將完成“監聽事件”的作業并執行我們給它的回呼。所以執行回呼的不是“我們”,而是事件監聽器。
在你的情況下,
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;
您無法“立即”獲得 the 的hash價值user.password,更糟糕的是,您甚至不知道它何時會到來。您的程式會停止并等到bcrypt完成其業務嗎?如果你有很多async功能,你的程式將是奧運會最慢冠軍的最愛。
這些承諾是怎么回事,我們怎么能不被貼上老年專案的標簽?
承諾
這是一個快速/長評論,試圖解釋承諾的概念。
在“正常”代碼中,每一行代碼都會在下一行代碼之前執行并“完成”。例如:(帶烹飪)
- 將黃油和糖混合,
- 一次加一個雞蛋,等等。
或在您的代碼中:
let user = this as UserDocument;
if(!user.isModified('password')){
return next();
}
承諾是在下一行代碼之前執行但未完成的特定代碼。前任:
- 當蛋糕在烤箱里時(承諾),
- you prepare the frosting,
- but you can't put it until the cake in baked (the "then" action of promises).
Note: Your code is using await so there is no "explicit" then method.
You will have many example of "promises" things in everyday life. you may have heard of asynchronous code = not one after the other, not in sync, ...
- Turning on an alarm to wake you in the morning,
thenyou make the promise that you will not ignore it; - putting a reminder on the calendar
thenyou make the promise that you will go to that job interview; etc.
All the while, you continue with your life after making those promises.
In code, a function that returns a promise will have a then method where you tell the computer what to do when when the "alarms goes off".
It is usually written like this
mypromise().then(doThisThingFunction)
const continueWithMyLife = true
In this way the then method is very similar to the callback of node.js. It is just expressed in a different way in the code and is not specific to node (callbacks are also not specific to node...).
One very important difference between them is that callbacks are something that the listener "do" and promises is something that resolves (hopefully) to a returning value.
Async/Await
Nowadays it is common to use async/await. Fortunately/unfortunately it basically hides the asynchronous behaviour. Better flow of reading the code, but also much worse understanding of promises for new programmers.
After a await, there is no then method (Or you could say that the following line of code is the then action). There is no "continuing with your life". There is only "waiting until the alarms goes off", So the next line after the await is essentially the "get out of the bed action".
That is why, in your code, the hash value is available in the next line. Basically in the "old way" to write promises
user.password = hash;
would be inside the then function.
And that is also why it is returning Promise<void>
But still, all these analogies won't really help. The best is to try it in everyday code. There is nothing like experience to understand anything.
轉載請註明出處,本文鏈接:https://www.uj5u.com/gongcheng/424008.html
上一篇:MongoError:無法識別的運算式'$last'(“版本”:“5.0.4”)
下一篇:有誰知道這個錯誤的修復方法(TypeError:無法分配給物件'#<QueryCursor>'的只讀屬性'map')
