如何承诺node.js net.connect(与bluebird)

How to Promisify node.js net.connect (with bluebird)?

本文关键字:net connect bluebird js node 承诺 何承诺      更新时间:2023-09-26

我想要node.js函数net.connect的Promise版本。如果连接成功,则应使用套接字解决Promise,如果存在连接错误,则应错误拒绝Promise,最好它也应是可取消的,其中取消将停止连接尝试。

我自己做了一个快速尝试,但还没有实现取消:

function connectAsync() {
    var connect_args = arguments;
    return new Promise(function (resolve, reject) {
        var socket = net.connect.apply(this, connect_args);
        socket.once('connect', function () {
            socket.removeListener('error', reject);
            resolve(socket);
        });
        socket.once('error', function (err) {
            socket.removeListener('connection', resolve);
            reject(err);
        });
    });
}

然而,对于这样一件简单的事情来说,这似乎非常复杂。有更好的方法吗?有人已经这么做了吗?

如果你直接看的话,总而言之——EventEmitters是一个非常复杂的抽象。

Promise表示排序操作——可以将其视为赋值运算符或分号。常规同步编程中的代码看起来像:

try{
    var value = foo(bar);
    log(value);
} catch(e){
    // handle error
}

事情一个接一个地运行:

  1. 输入试块
  2. 使用参数bar运行foo
  3. 记录该值,除非出现错误
  4. 如果出现错误,请进行处理

这就像一个长的单链操作。一个承诺就是这样的:

 fooAsync(bar).
 then(log).
 catch(function(){
      // handle error
 });

诺言是一条链子。您可以创建几个这样的链,这与表示执行一系列操作的其他形式的并发(如线程)类似。它看起来像以下内容:

--------------------------------+-成功------------------>

                   --Error---->// might join up

另一方面,事件发射器不能保证它发射的事件的名称或类型,节点EventEmitter有一些很酷的功能(如堆栈跟踪和error事件),但有一个比承诺弱得多的约定-不同的事件发射器发射不同的事件,事件发射器可以做这样的事情:

----Foo fired-+-Handler 1    ---- Bar fired-+      ---- Baz Fired-+-Handler 1
              --Handler 2                                         --Handler 2

它不是一个单一的链条,因此,尽管已经对此进行了几次尝试和讨论,但不存在表示事件发射器承诺的通用方法,它们在事件处理和事件名称方面差异太大。

另一方面,pg.connect采用节点式的错误回调。因此,承诺很容易,这些都是非常明确的,并遵守合同。

您所拥有的是好的,并且您可以将其推广为具有两个事件的事件发射器。记住,你只写过一次这种样板,然后在你的代码中使用它:)

您可以删除两个removeListener()行。承诺只能被解决或拒绝一次,这样你就不必担心你的活动再次被调用。承诺一旦兑现就不会改变它的状态。

而且,我认为你有几个问题需要解决:

  1. var connect_args = arguments可能不起作用,因为arguments是一种时髦类型的时间对象。通常的工作是复制它的内容:var connect_args = [].slice.call(arguments);

  2. 在这一行中,net.connect.apply(this, connect_args);,我认为this不会是正确的值,因为此时您处于promise回调中(在这种特殊情况下可能无关紧要)。使用net.connect.apply(net, connect_args);可能在技术上更正确,它将更直接地模拟调用net.connect(args)

至于使用承诺是否明智,你在评论中似乎对此事有一些看法。

除了删除removeListener()行代码之外,我认为没有太多方法可以简化这一点。您正在创建一个承诺来响应两个不同的自定义条件,因此您必须编写代码来检测这两个条件。没办法。

附言:如果您不删除removeListener()行代码,您可能会出现错误,因为您正在为'connect'设置事件,但正在执行removeListener('connection)。此外,我不知道为什么要将函数传递给removeListener(),因为它与建立事件处理程序时使用的函数引用不同。

我提出的解决方案与您得到的解决方案几乎相同:

p = new Promise((resolve, reject) ->
    listener = (data) ->
        try
            check_data_format(data)
        catch err
            return reject(err)
        if is_right_data(data)
            return resolve()
    ee.on("stdout", listener)
)
return p

偶尔当事情变得更不愉快时:

reject_f = null
resolve_f = null
p = new Promise((resolve, reject) ->
    reject_f = reject
    resolve_f = resolve
)
listener = (data) ->
    try
        check_data_format(data)
    catch err
        return reject(err)
    if is_right_data(data)
        return resolve()
ee.on("stdout", listener)

我在这里提出了一个问题(要求提供文件),但被重定向到了你的问题。

我得出的结论是,目前承诺和事件发射器的交叉点非常丑陋,我必须接受它。我没有遇到比我们独立发明的更好的建议,所以如果你有,请分享。