当链接Q.ninvoke时类型错误。

时间:2021-07-20 15:14:46

There is an error when I was trying to use Q to chain mongodb functions in Node.js as follow:

当我尝试在Node中使用Q来链接mongodb函数时,出现了一个错误。js:

Q.ninvoke(MongoClient, 'connect', 'mongodb://127.0.0.1:27017/mydb')
.then(function(db){
    return Q
    .ninvoke(db, 'createCollection', 'mycollection')
    .ninvoke(db.collection('mycollection'), 'createIndex', {id: 1}) // error occurs here
    .ninvoke(db, 'close')
    .then(function(){...})
});

The error message I got:

我得到的错误信息:

TypeError: Cannot call method 'apply' of undefined
  at Promise.post (/path/to/my/project/node_modules/q/q.js:1157:36)
  at Promise.promise.promiseDispatch (/path/to/my/project/node_modules/q/q.js:784:41)
  at /path/to/my/project/node_modules/q/q.js:600:44
  at runSingle (/path/to/my/project/node_modules/q/q.js:133:13)
  at flush (/path/to/my/project/node_modules/q/q.js:121:13)
  at process._tickCallback (node.js:442:13)

According to the message, line 1157 in q.js is about:

根据消息,第1157行在q。js是关于:

Q.fulfill = fulfill;
function fulfill(value) {
    return Promise({
    ...
    "post": function (name, args) {
        if (name === null || name === void 0) {
            return value.apply(void 0, args);
        } else {
            return value[name].apply(value, args); // error occurs here: line 1157
        }
    }

Although I have little idea what's going on inside Q, I guess db.collection('mycollection') is not passed correctly as value in line 1157. I've raised this issue on the Github of q but haven't got any response.

虽然我不知道Q里面发生了什么,但我想db.collection(“mycollection”)在第1157行没有正确传递。我在Github上提出了这个问题,但是没有得到任何回应。

If I change the code like this, everything works fine again:

如果我像这样修改代码,一切都会恢复正常:

Q.ninvoke(MongoClient, 'connect', 'mongodb://127.0.0.1:27017/mydb')
.then(function(db){
    return Q
    .ninvoke(db, 'createCollection', 'mycollection')
    .then(function(){
        return Q.ninvoke(db.collection('mycollection'), 'createIndex', {id: 1}) // no error this time
        .then(function(){
             return Q.ninvoke(db, 'close').then(function(){...});
        });
    });
});

However, here comes a pyramid which grows along with the chain. I think Q should have supported chaining ninvoke like the first example.

然而,这里有一个与链条一起生长的金字塔。我认为Q应该像第一个例子一样支持链接ninvoke。

In short, my question is whether there is misunderstanding the of use Q, or there is actually a bug in Q?

简而言之,我的问题是,是否存在对Q的误解,或者在Q中存在一个bug ?

Package versions I used: node.js: v0.10.36 Q: 1.4.0 mongodb: 2.0.31

我使用的包版本:节点。js: v0.10.36 Q: 1.4.0 mongodb: 2.0.31。

Update

I rule out the factor of MongoDB and narrow down the scope of the problem as follow:

我排除了MongoDB的因素,并将问题的范围缩小如下:

var TestClass = function (name){
};

TestClass.prototype.printName = function (callback) {
    console.log('printName called');
    return callback(null);
};

var test = new TestClass('test object');

test.printName(function (err) {
    test.printName(function (err) {
        console.log('callback called');
    });
});

In this case, the output should be:

在这种情况下,输出应该是:

$ node q-test.js
printName called
printName called
callback called

But if I use Q as follow:

但如果我用Q来表示

Q.ninvoke(test, 'printName')
.ninvoke(test, 'printName')
.then(function(){
    console.log('callback called');
})
.done();

It turns out the output with an error like this:

它输出的结果是这样的:

$ node test.js
printName called

/path/to/my/project/node_modules/q/q.js:155
                throw e;
                      ^
TypeError: Cannot read property '[object Object]' of undefined
    at Promise.post (/path/to/my/project/node_modules/q/q.js:1161:29)
    at Promise.promise.promiseDispatch (/path/to/my/project/node_modules/q/q.js:788:41)
    at /path/to/my/project/node_modules/q/q.js:556:49
    at runSingle (/path/to/my/project/node_modules/q/q.js:137:13)
    at flush (/path/to/my/project/node_modules/q/q.js:125:13)
    at process._tickCallback (node.js:442:13)
    at Function.Module.runMain (module.js:499:11)
    at startup (node.js:119:16)
    at node.js:929:3

1 个解决方案

#1


2  

TL;DR version: When called in a chain as opposed to directly as Q.ninvoke() the object whose function is to be invoked comes from the result of the prior function in the chain, not from the first parameter to the qpromise.ninvoke() call.

版本:当在一个链中调用,而不是直接调用Q.ninvoke()时,函数被调用的对象来自于链中先前函数的结果,而不是从第一个参数到qpromise.ninvoke()调用。

Elaboration: While I initially thought from a quick glance that the issue was that in your original code, the collection itself would not yet have been created when db.collection('mycollection') is called in the following line. In your correct/working version, this would be addressed, because the ninvoke binding isn't happening until after the collection has been created. I now see that the general problem is how you are calling .ninvoke() past its initial use in a chain.

详细说明:虽然我最初认为问题是在您的原始代码中,但在下面一行中调用了db.collection(“mycollection”)时,集合本身还没有创建。在您的正确/工作版本中,这将被解决,因为在创建集合之后,ninvoke绑定不会发生。我现在看到,一般的问题是您如何调用.ninvoke()过去它在链中的最初使用。

Your updated example wasn't directly helpful, because you are using ninvoke() but not using a node-style callback form with it, so you were bound to get different sort of errors.

您的更新的示例并不是直接有用的,因为您使用的是ninvoke(),而不是使用节点样式的回调表单,因此您肯定会得到不同类型的错误。

At least referring to your new non-Mongo example the root problem is how you are using .ninvoke() in your subsequent calls. After the initial Q.ninvoke() the subsequent .ninvoke() calls when applied to the returned promise, uses the return value of the promise as the first parameter. This is maybe more easily illustrated than anything else, please review the change I made to your "printName" example:

至少在您的新非mongo示例中,根问题是您在后续调用中如何使用.ninvoke()。在初始Q.ninvoke()之后,在应用到返回的承诺时调用的。ninvoke()调用,使用承诺的返回值作为第一个参数。这可能比其他任何东西更容易说明,请查看我对您的“printName”示例所做的更改:

var TestClass = function (name){ };

TestClass.prototype.printName = function (callback) {
    console.log('printName called');
    return callback(null,this);     // node style callback, also return this
};

var test = new TestClass('test object');

Q.ninvoke(test, 'printName')
//  in the lines below, the result of the prior line is the "implied" first
// parameter to ninvoke();
.ninvoke('printName')
.ninvoke('printName')   
.then(function(){
    console.log('callback called');
})
.done();

Hopefully this illustrates what is going on.

希望这能说明发生了什么。

Returning to your original quesiton, while I am not a Mongo user, from looking at the docs for it, it appears that createCollection in fact returns the collection, so you could probably amend your earlier code:

回到最初的问题,而我不是Mongo用户,从查看文档来看,似乎createCollection实际上返回了集合,所以您可能会修改您的早期代码:

return Q
.ninvoke(db, 'createCollection', 'mycollection')
.ninvoke(db.collection('mycollection'), 'createIndex', {id: 1}) // error occurs here

Into

return Q
.ninvoke(db, 'createCollection', 'mycollection')
.ninvoke('createIndex', {id: 1}) // error *should not occur* here :-)

However, I am not sure what you will want to do about the "close" since presumably createIndex will not just return a reference to the DB. I don't know the API but presumably you could use Q's .get() or related functions to get to it and then pass along to the .ninvoke('close').

但是,我不确定您想要对“close”做什么,因为假设createIndex将不仅仅返回对DB的引用。我不知道API,但假设您可以使用Q .get()或相关函数来访问它,然后传递到.ninvoke('close')。

See this issue for a discussion of the implementation in Q, which also has a link to the changes made to implement it and you can see exactly how it is working.

请参见这个问题,以讨论在Q中实现的问题,这也与实现它所做的更改有关联,您可以清楚地看到它是如何工作的。

#1


2  

TL;DR version: When called in a chain as opposed to directly as Q.ninvoke() the object whose function is to be invoked comes from the result of the prior function in the chain, not from the first parameter to the qpromise.ninvoke() call.

版本:当在一个链中调用,而不是直接调用Q.ninvoke()时,函数被调用的对象来自于链中先前函数的结果,而不是从第一个参数到qpromise.ninvoke()调用。

Elaboration: While I initially thought from a quick glance that the issue was that in your original code, the collection itself would not yet have been created when db.collection('mycollection') is called in the following line. In your correct/working version, this would be addressed, because the ninvoke binding isn't happening until after the collection has been created. I now see that the general problem is how you are calling .ninvoke() past its initial use in a chain.

详细说明:虽然我最初认为问题是在您的原始代码中,但在下面一行中调用了db.collection(“mycollection”)时,集合本身还没有创建。在您的正确/工作版本中,这将被解决,因为在创建集合之后,ninvoke绑定不会发生。我现在看到,一般的问题是您如何调用.ninvoke()过去它在链中的最初使用。

Your updated example wasn't directly helpful, because you are using ninvoke() but not using a node-style callback form with it, so you were bound to get different sort of errors.

您的更新的示例并不是直接有用的,因为您使用的是ninvoke(),而不是使用节点样式的回调表单,因此您肯定会得到不同类型的错误。

At least referring to your new non-Mongo example the root problem is how you are using .ninvoke() in your subsequent calls. After the initial Q.ninvoke() the subsequent .ninvoke() calls when applied to the returned promise, uses the return value of the promise as the first parameter. This is maybe more easily illustrated than anything else, please review the change I made to your "printName" example:

至少在您的新非mongo示例中,根问题是您在后续调用中如何使用.ninvoke()。在初始Q.ninvoke()之后,在应用到返回的承诺时调用的。ninvoke()调用,使用承诺的返回值作为第一个参数。这可能比其他任何东西更容易说明,请查看我对您的“printName”示例所做的更改:

var TestClass = function (name){ };

TestClass.prototype.printName = function (callback) {
    console.log('printName called');
    return callback(null,this);     // node style callback, also return this
};

var test = new TestClass('test object');

Q.ninvoke(test, 'printName')
//  in the lines below, the result of the prior line is the "implied" first
// parameter to ninvoke();
.ninvoke('printName')
.ninvoke('printName')   
.then(function(){
    console.log('callback called');
})
.done();

Hopefully this illustrates what is going on.

希望这能说明发生了什么。

Returning to your original quesiton, while I am not a Mongo user, from looking at the docs for it, it appears that createCollection in fact returns the collection, so you could probably amend your earlier code:

回到最初的问题,而我不是Mongo用户,从查看文档来看,似乎createCollection实际上返回了集合,所以您可能会修改您的早期代码:

return Q
.ninvoke(db, 'createCollection', 'mycollection')
.ninvoke(db.collection('mycollection'), 'createIndex', {id: 1}) // error occurs here

Into

return Q
.ninvoke(db, 'createCollection', 'mycollection')
.ninvoke('createIndex', {id: 1}) // error *should not occur* here :-)

However, I am not sure what you will want to do about the "close" since presumably createIndex will not just return a reference to the DB. I don't know the API but presumably you could use Q's .get() or related functions to get to it and then pass along to the .ninvoke('close').

但是,我不确定您想要对“close”做什么,因为假设createIndex将不仅仅返回对DB的引用。我不知道API,但假设您可以使用Q .get()或相关函数来访问它,然后传递到.ninvoke('close')。

See this issue for a discussion of the implementation in Q, which also has a link to the changes made to implement it and you can see exactly how it is working.

请参见这个问题,以讨论在Q中实现的问题,这也与实现它所做的更改有关联,您可以清楚地看到它是如何工作的。