【问题标题】:Promises seem to not be called in correct order?承诺似乎没有按正确的顺序调用?
【发布时间】:2020-04-22 22:55:28
【问题描述】:

我正在尝试重写我编写的用于为 MongoDB 数据库提供种子的模块。它最初可以很好地处理回调,但我想转向 Promises。但是,执行和结果似乎没有任何意义。

Seeder 对象中有三个通用函数:

// functions will be renamed
Seeder.prototype.connectPromise = function (url, opts) {
    return new Promise((resolve,reject) => {
        try {
            mongoose.connect(url, opts).then(() => {
                const connected = mongoose.connection.readyState == 1
                this.connected = connected
                resolve(connected)
            })
        } catch (error) {
            reject(error)
        }
    })
}
[...]
Seeder.prototype.seedDataPromise = function (data) {
    return new Promise((resolve,reject) => {
        if (!this.connected) reject(new Error('Not connected to MongoDB'))
        // Stores all promises to be resolved
        var promises = []
        // Fetch the model via its name string from mongoose
        const Model = mongoose.model(data.model)
        // For each object in the 'documents' field of the main object
        data.documents.forEach((item) => {
            // generates a Promise for a single item insertion.
            promises.push(promise(Model, item))
        })
        // Fulfil each Promise in parallel
        Promise.all(promises).then(resolve(true)).catch((e)=>{
            reject(e)
        })
    })
}
[...]
Seeder.prototype.disconnect = function () {
    mongoose.disconnect()
    this.connected = false
    this.listeners.forEach((l) => {
        if (l.cause == 'onDisconnect') l.effect()
   })
}

代码的主要逻辑没有问题。我可以让它正确地播种数据。然而,当使用 Promises 时,数据库在其他任何事情完成之前就断开连接,尽管断开连接函数被称为 .finally()

我正在像这样运行这些函数:

Seeder.addListener('onConnect', function onConnect () { console.log('Connected') })
Seeder.addListener('onDisconnect', function onDisconnect () {console.log('Disconnected')})

Seeder.connectPromise(mongoURI, options).then(
    Seeder.seedDataPromise(data)
    ).catch((error) => {  <-- I am catching the error, why is it saying its unhandled?
        console.error(error)
}).finally(Seeder.disconnect())

输出是这样的:

Disconnected
(node:14688) UnhandledPromiseRejectionWarning: Error: Not connected to MongoDB
    at Promise (C:\Users\johnn\Documents\Code\node projects\mongoose-seeder\seed.js:83:37)

坦率地说,这对我来说没有意义,正如我在调用reject() 的堆栈跟踪中指出的那样。并且处理了这个拒绝,因为我有一个如上所示的 catch 语句。此外,我不明白为什么数据库甚至没有机会连接,因为 finally() 块应该最后调用。

除了其他建议外,解决方案是返回Promise.all 调用。

【问题讨论】:

  • 你的 connectPromise() 函数是一个 Promise 反模式。没有理由将现有的 Promise 包装在手动创建的 Promise 包装器中。这被认为是一种承诺反模式。只需执行return mongoose.connect().then();。这会很好地回报你的承诺。
  • 请不要为您的问题添加解决方案。 Stack Overflow 的格式是问题出现在问题部分,答案出现在答案部分。
  • 没有。对于任何搜索的人来说,立即看到答案更有用。
  • 没有。这不是 Stack Overflow 的工作方式 (ref)。我已恢复您的更改。

标签: node.js mongoose promise


【解决方案1】:

您将错误的参数传递给thenfinally。首先在这里:

Seeder.connectPromise(mongoURI, options).then(
    Seeder.seedDataPromise(data)
)

您实际上不是将回调函数传递给then,而是当场执行该函数(因此无需等待承诺解决并触发then回调——这不是回调)。

你应该这样做:

Seeder.connectPromise(mongoURI, options).then(
    () => Seeder.seedDataPromise(data)
)

这里出现了类似的错误:

finally(Seeder.disconnect())

应该是:

finally(() => Seeder.disconnect())

Promise 构造函数反模式

与您的问题无关,但您正在通过使用 new Promise 创建新的承诺来实现反模式,而实际上您已经通过使用 mongodb API 获得了承诺。

例如,您在此处执行此操作:

Seeder.prototype.connectPromise = function (url, opts) {
    return new Promise((resolve,reject) => {
        try {
            mongoose.connect(url, opts).then(() => {
                const connected = mongoose.connection.readyState == 1
                this.connected = connected
                resolve(connected)
            })
        } catch (error) {
            reject(error)
        }
    })
}

但是使用new 创建的包装承诺只是一个包装器,没有添加任何有用的东西。随便写:

Seeder.prototype.connectPromise = function (url, opts) {
    return mongoose.connect(url, opts).then(() => {
        const connected = mongoose.connection.readyState == 1
        this.connected = connected
        return connected;
    });
}

同样的情况发生在你的下一个原型函数中。我将留给您在此处应用类似的简化,因此请避免使用 promise constructor antipattern


在稍后对您问题的编辑中,您包含了此更改,但您没有在该函数中返回承诺。在此处添加return

  return Promise.all(promises).then(() => {
//^^^^^^
      return true
  }).catch(() => {
      console.log(`Connected:\t${this.connected}`)
  })

【讨论】:

  • 我做了一些改变,它建立了与数据库的连接,调用了播种函数,但在seedData完成之前仍然调用了断开连接。
  • 那是因为你必须return Promise.all(...,否则你的函数不会返回一个promise。
  • 明白了!谢谢!我将检查我的所有代码,以确保我完全理解它并且它尽可能干净
猜你喜欢
  • 1970-01-01
  • 2015-12-22
  • 2021-01-31
  • 1970-01-01
  • 2020-06-01
  • 2019-06-16
  • 1970-01-01
  • 1970-01-01
  • 2017-04-23
相关资源
最近更新 更多