【问题标题】:NodeJs: util.promisify where the callback function has multiple argumentsNodeJs:util.promisify,其中回调函数有多个参数
【发布时间】:2019-01-04 12:48:07
【问题描述】:

我可能在这里遗漏了一些非常明显的东西,但是如何将util.promisify 与看起来像这样的函数一起使用?

function awkwardFunction (options, data, callback) {
    // do stuff ...
    let item = "stuff message"
    return callback(null, response, item)
}

我可以这样称呼:

 awkwardFunction ({doIt: true},'some data', (err, result, item) => {
      console.log('result')
      console.log(result)
      console.log('item')
      console.log(item)
      done()
    })

然后回来

result
{ data: 'some data' }
item
stuff message

使用承诺版本时:

let kptest = require('util').promisify(awkwardFunction)
kptest({doIt: true},'some data')
   .then((response, item) => {
    console.log('response')
    console.log(response)
    console.log('item')
    console.log(item)

 })
 .catch(err => {
     console.log(err)
  })

并试图同时访问“响应”和“项目”,似乎第二个参数被忽略了......

result
{ data: 'some data' }
item
undefined

有没有办法在不改变函数的情况下做到这一点(实际上,它是一个库函数,所以我不能)。

【问题讨论】:

    标签: node.js


    【解决方案1】:

    util.promisify 旨在与带有function (err, result): void 签名的节点样式回调一起使用。

    可以手动处理多个参数:

    let kptest = require('util').promisify(
      (options, data, cb) => awkwardFunction(
        options,
        data,
        (err, ...results) => cb(err, results)
      )
    )
    
    kptest({doIt: true},'some data')
    .then(([response, item]) => {...});
    

    如果需要更复杂的功能,可以使用pify 等第三方解决方案来代替util.promisify,它有multiArgs option 来解决这种情况。

    【讨论】:

    • 啊,好主意。我不知道为什么我从来没想过承诺一个匿名函数!
    • 只是添加 - 如果 promisifying 函数直接导致 typescript 选择错误的重载,这将非常有用。
    【解决方案2】:

    您可以制作自己的 Promisify,在其中返回一个使用回调参数解析的 Promise,然后在 then 块上解构它们。希望这会有所帮助。

    function awkwardFunction (options, data, callback) {
        // do stuff ...
        let item = "stuff message";
        return callback(null, data, item);
    }
    
    const mypromisify = (fn) =>
        (...args) =>
            new Promise(resolve =>
                fn(...args,
                    (...a) => resolve(a)
                )
            );
    
    const kptest = mypromisify(awkwardFunction);
    
    kptest({ doIt: true }, 'some data')
        .then(([error, response, item]) => {
            console.log(response);
            console.log(item);
        })
        .catch(err => {
            console.log(err);
        });

    【讨论】:

    • 也是一个好主意。解构对我来说有点新鲜,所以我从没想过这种方法。
    【解决方案3】:

    不可能有.then((response, item) => {,因为promise 代表单个值。但是你可以像这样.then(({response, item}) => { 一个带有两个字段的对象。

    您需要为该函数提供一个自定义的 Promisify 实现。

    const { promisify } = require('util')
    
    awkwardFunction[promisify.custom] = (options, data) => new Promise((resolve, reject) => {
      awkwardFunction(options, data, (err, response, item) => {
        if(err) { reject(err) }
        else { resolve({ response, item }) }
      })
    })
    
    const kptest = promisify(awkwardFunction)
    

    或者,如果这是该功能被承诺的唯一地方,您可以直接使用承诺版本const kptest = (options, data) => new Promise(...,无需额外的承诺步骤。

    【讨论】:

    • 哦 - 太棒了。我发现了 [promisify.custom] 但不知道如何使用它。
    • 难道[promisify.custom] 不会导致promisify 返回那个值而不是它原本会返回的值吗?如果是这样,我认为这相当于将自定义函数直接分配给kptest
    • @paulmelnikow 嗯,是的。这就是我在这里试图说的“或者如果这是唯一一个函数被承诺的地方,你可以直接使用承诺版本 const kptest = (options, data) => new Promise(... w/o 额外的承诺一步。”
    【解决方案4】:

    我只是卷起袖子进行心脏直视手术以实现这一目标,但我很高兴我发现有人已经这样做了。

    如果您使用BluebirdPromisify(它变得如此流行),那么实际上有一个{ multiArgs: true } 的标志,您可以通过它并在这里做您需要的事情! (Source)

    它将回调的多个参数转换为一个数组。因此,在我的MySQL's query 的情况下,默认回调具有(error, result, fields) 的3 个参数,使用典型的promisify 获取fields 是不可能的。但是随着 {multiArgs: true} 标志的传递,解析后的值将变成 [result, fields] 的数组。

    【讨论】:

      【解决方案5】:

      我无法决定我最喜欢哪种方法 - 所有 3 个答案都很棒。 Yury Tarabanko 可能是最“标准”的,Alex G 非常通用,而 estus 超级简单。

      我不想让这个问题“悬而未决”,因为这不是真的,而且对于寻找相同信息的其他人没有用处。

      如果有更好的处理方法,请版主告诉我!

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-09-27
        • 1970-01-01
        • 2018-03-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-01-08
        • 1970-01-01
        相关资源
        最近更新 更多