【问题标题】:How to convert callback code to async/await? [duplicate]如何将回调代码转换为异步/等待? [复制]
【发布时间】:2018-07-12 13:46:11
【问题描述】:

我正在一个基于 node express 的 Web 应用程序中为 MongoDB 使用 Mongoose ODM,该应用程序以回调方式提供其 API。这种方式创建了一个回调地狱来填充文档。

编辑1:我已经添加了当前的工作代码。

User.remove({}, (err) => {
  if (err) return console.error(err)

  let admin = new User({
    email: 'admin@dap.com',
    password: 'dapdap'
  })

  admin.save((err, admin) => {
    if (err) return console.error(err)

    console.info('Success')
    mongoose.disconnect()
  })

})

一般来说,有没有一种优雅的方法可以将回调转换为 javascript 中的异步/等待方式?而在猫鼬环境中呢?

【问题讨论】:

  • 任何示例代码?
  • @zabusa 我忘了添加代码。我已经添加了它。请检查:)

标签: javascript node.js mongodb asynchronous mongoose


【解决方案1】:

最近版本的 Mongoose 返回一个 Promise 并提供常规的回调样式模式。由于 async 函数是 Promises 的语法糖,因此您可以 await 调用 Mongoose 方法。

async function clearUsers () {
  try {
    await User.remove({})
    let admin = new User({
      email: 'admin@dap.com',
      password: 'dapdap'
    })
    await admin.save()
    console.info('Success')
    mongoose.disconnect()
  } catch (e) {
    console.error(e)
  }
}

注意事项:

  • async 函数 总是 返回一个 Promise。如果您没有从 async 函数返回任何内容,它仍会返回一个 Promise,该 Promise 会在该函数执行完成时解析,但不会解析为任何值。
  • async 函数中的 try/catch 与常规同步代码的工作方式相同。如果在try 主体throw任何 函数调用或返回一个拒绝 的Promise,则执行将在该行直接停止并继续到catch 主体。
  • 被拒绝的承诺“涓涓”函数调用链。这意味着最顶层的被调用者可以处理错误。请参阅以下示例:

这是一种应该避免的反模式,除非您绝对需要处理特定函数中的错误,可能是为了提供来自不同来源的返回值:

async function fn1 () {
  throw new Error("Something went wrong")
}

async function fn2 () {
  try {
    await fn1()
  } catch (e) {
    throw e
  }
}

async function fn3 () {
  try {
    await fn2()
  } catch (e) {
    throw e
  }
}

async function run () {
  try {
    await fn3()
  } catch (e) {
    console.error(e)
  }
}

上面可以像下面这样实现,但仍然会捕获错误,不会导致运行时恐慌/崩溃:

async function fn1 () {
  throw new Error("Something went wrong")
}

function fn2 () {
  return fn1()
}

function fn3 () {
  return fn2()
}

async function run () {
  try {
    await fn3()
  } catch (e) {
    console.error(e)
  }
}

上面的代码有多种写法都是有效的,所以我建议你去探索这些。

记住上面的例子,你的函数 clearUsers() 可以重写为:

async function clearUsers () {
  await User.remove({})
  let admin = new User({
    email: 'admin@dap.com',
    password: 'dapdap'
  })
  await admin.save()
  mongoose.disconnect()
}

然后可能以两种不同的方式调用;

通过与直接返回的 Promise 交互:

clearUsers()
  .then(() => {
    console.log('Success')
  })
  .catch((e) => {
    console.error(e)
  })

或者来自另一个异步函数:

(async function () {
  try {
    await clearUsers()
    console.log('Success')
  } catch (e) {
    console.error(e)
  }
})()

如果clearUsers() 函数中的任何 函数调用抛出,例如await admin.save(),执行将在该行停止并返回一个rejected Promise,该Promise 将被捕获在两个变体中对应的 catch 块中。

【讨论】:

    【解决方案2】:

    由于 mongoose 的默认 promise 库 mpromise 现在已弃用,因此您可以插入自己的 promise 库以使 mongoose 的操作与 await/async 一起工作。

    Mongoose 查询不是承诺。但是,它们确实有一个用于 yield 和 async/await 的 .then() 函数。如果您需要一个成熟的承诺,请使用 .exec() 函数。

    mongoose.Promise = require('bluebird'); bluebird promise
    // mongoose.Promise = global.Promise; // default js promise - 4x- time slower than bluebird
    
    async function removeUser () {
      try {
        await User.remove({}).exec();
        let admin = new User({
          email: 'admin@dap.com',
          password: 'dapdap'
        })
        await admin.save();
        } catch (e) {
            console.log(e);
        }
    }
    

    官方文档:mongoosejs.com/docs/promises.html

    【讨论】:

      【解决方案3】:

      使用 Bluebird 是处理 Promise 中回调的好选择,它在一定程度上更容易理解。

      不过我建议你试试

      Async.js

      它专为管理 javascript 的异步特性而构建。

      图书馆的This particular function 会做你想做的事。

      【讨论】:

        【解决方案4】:

        你可以使用 Promise:

        const userRemovePromise = new Promise((resolve, reject) => {
            User.remove({}, (err) => {
              if (err) reject();
        
              let admin = new User({
                email: 'admin@dap.com',
                password: 'dapdap'
              })
        
              admin.save((err, admin) => {
                if (err) reject();
        
                resolve();
              })
        
            });
        
        });
        
        userRemovePromise.then(function(){  
            mongoose.disconnect();
        });
        

        【讨论】:

          【解决方案5】:

          您在上面编写的相同代码可以编写为 Promise。来自原始文档,

          “Mongoose 查询不是 Promise。但是,它们确实有一个用于 yield 和 async/await 的 .then() 函数。如果您需要一个成熟的 Promise,请使用 .exec() 函数。”

          因此您可以按如下方式使用异步等待模式,

          async function removeUser () {
              // Surround the following call with a try catch
              await User.remove({});
          }
          

          编辑:

          这里的一个答案是关于插入蓝鸟承诺。虽然这是一个好方法,但它是可选的。你可以在不插入自己的 Promise 库的情况下做同样的事情。

          【讨论】:

            【解决方案6】:

            你能配置猫鼬来使用蓝鸟吗?

            mongoose.Promise = require('bluebird');

            然后你就可以花点心思去做:

            User.remove().then(() => {
                let admin = new User({ ... });
            
                admin.save().then((admin) => {
            
                }).catch((err) => {
                  //Saving admin error handling
                });
            }).catch((err) {
              //User remove error handling
            });
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2020-12-21
              • 1970-01-01
              • 2018-12-30
              • 1970-01-01
              • 2021-05-17
              • 2015-12-13
              • 2018-09-04
              相关资源
              最近更新 更多