仅返回Promise的函数与使用async关键字声明的函数之间存在很小但非常重要的区别。
看一下以下代码片段:
function fn(obj) {
const someProp = obj.someProp
return Promise.resolve(someProp)
}
async function asyncFn(obj) {
const someProp = obj.someProp
return Promise.resolve(someProp)
}
asyncFn().catch(err => console.error('Catched')) // => 'Catched'
fn().catch(err => console.error('Catched')) // => TypeError: Cannot read property 'someProp' of undefined
正如你看见的,以上两个函数有相同的函数体,我们试图在函数里访问argument,但是实际上2个argument都是undefined,两个函数唯一不同的是,asyncFn函数被关键字async声明为异步函数
这意味着js将会确定asyncFn函数将会返回一个promise(无论是resolved还是rejected),即使函数体里发生了错误,在我们的案例中是.catch代码块。
然而,引擎不知道fn函数也返回一个promise,因此它不会调用catch()
A more real-world version
我知道你现在正在想什么:
我什么时候会犯这样的错误?
对吗?
让我们写一个像刚才那样简单的应用。
假设我们有一个使用MongoDB的Express应用程序。如果你相信我,我已经把代码放在了this github repo
,所以你可以clone下来,然后在本地运行,但是我也会把代码粘贴在下面:
app.js
// app.js
'use strict'
const express = require('express')
const db = require('./db')
const userModel = require('./models/user-model')
const app = express()
db.connect()
app.get('/users/:id', (req, res) => {
return userModel
.getUserById(req.params.id)
.then(user => res.json(user))
.catch(err => res.status(400).json({ error: 'An error occured' }))
})
app.listen(3000, () => console.log('Server is listening'))
仔细看,.catch代码块在route中定义了,这个地方会有魔术发生。
db.js 被用来链接mongo数据库和获取mongobd连接:
'use strict'
const MongoClient = require('mongodb').MongoClient
const url = 'mongodb://localhost:27017'
const dbName = 'async-promise-test'
const client = new MongoClient(url)
let db
module.exports = {
connect() {
return new Promise((resolve, reject) => {
client.connect(err => {
if (err) return reject(err)
console.log('Connected successfully to server')
db = client.db(dbName)
resolve(db)
})
})
},
getDb() {
return db
}
}
最后,我们看我的user模块,只有一个getUserById函数:
// models/user-model.js
'use strict'
const ObjectId = require('mongodb').ObjectId
const db = require('../db')
const collectionName = 'users'
module.exports = {
/**
* Get's a user by it's ID
* @param {string} id The id of the user
* @returns {Promise<Object>} The user object
*/
getUserById(id) {
return db
.getDb()
.collection(collectionName)
.findOne({ _id: new ObjectId(id) })
}
}
如果您回头查看app.js文件,你会发现访问url:localhost:3000/users/<id>时会调用getUserById,
当你访问localhost:3000/users/1.你觉得会发生什么?
好吧,如果您回答:“我将从mongo那里收到一个巨大的错误”-您是对的。确切地说,您会收到如下错误:
Error: Argument passed in must be a single String of 12 bytes or a string of 24 hex characters
您怎么看,这个.catch块会被调用吗?
// app.js
// ... stuff ...
app.get('/users/:id', (req, res) => {
return userModel
.getUserById(req.params.id)
.then(user => res.json(user))
.catch(err => res.status(400).json({ error: 'An error occured' })) // <=== THIS ONE HERE!
})
// ... stuff ...
没有
丝毫没有
如果将函数声明更改为此,会发生什么?
module.exports = {
// Note that async keyword right there!
async findById(id) {
return db
.getDb()
.collection(collectionName)
.findOne({ _id: new ObjectId(id) })
}
}
是的,您已经掌握了一切。我们的.catch()块将被调用,并且我们将以一个不错的json错误响应用户。
我希望对于您中的某些人来说,这些信息是新的。注意:这篇文章中我没有试图让你总是用async函数,尽管它们非常酷。他们有用例,但仍然只是Promises上的语法糖。
我只是想让您知道,有时候这些问题可能会花很多时间,当您遇到上述错误时,您可能知道问题的出处。
网友评论