Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
206 views
in Technique[技术] by (71.8m points)

javascript - How to ensure correct "this" with Promise.promisify?

I am trying to refactory my nodejs server using promises with Bluebird library, but I am stuck in a simple problem.

After to get the users from my db, I want to list all notification class associated with this user:

Bad Way (working...)

adapter.getUsers(function(users){
    users.rows.forEach(function(item){
        user = item.username;
        adapter.getNotifications(user, function(notificationList){
            console.log(notificationList);
        })
    });
});

Elegant Tentative Way (not working...)

var getNotifications = Promise.promisify(adapter.getNotifications);
adapter.getUsers().then(function(users) {
    users.rows.forEach(function(item){
        var dbUser = "sigalei/" + item.value.name;
        console.log(dbUser);
        return getNotifications(dbUser);
    });
}).then(function(result){
    console.log(result);
    console.log("NOTIFICATIONLIST");
});

However when I execute this code I get this error inside my getNotification method:

Unhandled rejection TypeError: Cannot read property 'nano' of undefined at Adapter.getNotifications (/Users/DaniloOliveira/Workspace/sigalei-api/api/tools/couchdb-adapter.js:387:30) at tryCatcher (/Users/DaniloOliveira/Workspace/sigalei-api/node_modules/bluebird/js/main/util.js:26:23)

EDIT After the user2864740`s precious comments, I noticed that the error is related with some scope problem. So, why after to use promisify method, the method dont getNotifications recognize the "this" env variable?

var Adapter = module.exports = function(config) {
    this.nano = require('nano')({
        url: url,
        request_defaults: config.request_defaults
    });
};

Adapter.prototype.getNotifications = function(userDb, done) {

    var that = this;
    console.log(that);
    var userDbInstance = that.nano.use(userDb);
    userDbInstance.view('_notificacao', 'lista',
      {start_key: "[false]", end_key: "[false,{}]"},
      function(err, body) {
        if(err){ done(err); }
        done(body);
    });

};
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

This is just the very common problem of calling "unbound" methods.
You can pass the context as an option to Promise.promisify to have it bound:

var getNotifications = Promise.promisify(adapter.getNotifications, {context: adapter});

Alternatively, you'd need to .bind() the method, or call the new getNotifications function on the adapter (using .call()). You might also consider using Promise.promisifyAll(adapater) and then just calling adapter.getNotificationsAsync(…).

Notice that this still doesn't work. You cannot simply create promises in a loop - you need to await them explicitly and return a promise from the then callback, otherwise just the undefined value you returned will be passed to the next callback immediately.

adapter.getUsers().then(function(users) {
    return Promise.all(users.rows.map(function(item){
        var dbUser = "sigalei/" + item.value.name;
        console.log(dbUser);
        return getNotifications(dbUser);
    }));
}).then(function(results) {
    for (var i=0; i<results.length; i++)
        console.log("result:", results[i]);
});

Instead of Promise.all(users.rows.map(…)), in Bluebird you can also use Promise.map(users.rows, …).


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...