异步函数vs返回promise的函数

2019-11-17  本文已影响0人  范小饭_

Async function vs. a function that returns a Promise
翻译:范小饭

仅返回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上的语法糖。

我只是想让您知道,有时候这些问题可能会花很多时间,当您遇到上述错误时,您可能知道问题的出处。

上一篇 下一篇

猜你喜欢

热点阅读