使用Promise时挂起变量的最佳实践

Amb*_*mps 5 javascript node.js promise

我是Promises的新手,我想知道最佳做法是在保持变量的同时保持变量?

通过Promise连接到MongoDB非常简单:

connectToMongoDB(data).done(function(db) {

    var collection = db.collection('test_inserts');
    // do more stuff here

});
Run Code Online (Sandbox Code Playgroud)

但是,如果我必须连接到两个不同的数据库会发生什么?

connectToMongoDB1(data1).then(function(db1) {

    return connectToMongoDB2(data2);

}).done(function(db2) {

    var collection = db1.collection('test_inserts');
    // ERROR: db1 is undefined

});
Run Code Online (Sandbox Code Playgroud)

这个错误非常有意义.但是如何在db1不改变我的connectToMongoDB2()功能的情况下前进,因为我想要保持connectToMongoDB2()并且我的所有承诺一般都非常通用?

我的意思是,我可以包装一个存储所有相关内容的对象,但这看起来有点像hacky:

var relevantStuff = {};

connectToMongoDB1(data1).then(function(db1) {

    relevantStuff.db1 = db1;
    return connectToMongoDB2(data2);

}).done(function(db2) {

    var collection = relevantStuff.db1.collection('test_inserts');
    // do more stuff here

});
Run Code Online (Sandbox Code Playgroud)

什么是最佳做法?

Flo*_*ine 12

注意:我在这个答案中使用蓝鸟.

有三种方法可以做你想要的:闭包,绑定和Promise.using.

封闭是@Sukima展示的方式.

function promisedFunc() {
    var db;
    return getCollection().then(function(col) {
        db = col;
        return db.query(stuff);
    }).then(function() {
        return db.query(otherStuff);
    });
}
Run Code Online (Sandbox Code Playgroud)

绑定:使用Promise.bind,您可以创建this一个包含值的对象.

function promisedFunc() {
    return getCollection().bind({}).then(function(col) {
        this.db = col;
        return this.db.query(stuff);
    }).then(function() {
        return this.db.query(otherStuff);
    });
}
Run Code Online (Sandbox Code Playgroud)

最后,bluebird v2引入的最后一种方法是使用真正的资源管理.

function promisedFunc() {
    return Promise.using(getDb(), function(db) {
        return db.query(stuff).then(function() {
            return db.query(otherStuff);
        });
    });
}
Run Code Online (Sandbox Code Playgroud)

我将getDb进一步解释这个方法.


最后一种方式提供了另一个非常有趣的好处:处理资源.例如,您经常需要close为数据库资源调用方法.Promise.using让你创建处理器,一旦解决(或不解决)其中的承诺就运行.

要了解为什么这是一个优势,让我们回顾一下这3种方法.

关闭:

var db, close;
return getCollection().spread(function(col, done) {
    db = col;
    close = done;
    return db.query(stuff);
}).then(function() {
    return db.query(otherStuff);
}).finally(function() {
    close();
});
Run Code Online (Sandbox Code Playgroud)

是的,这意味着每次使用数据库连接时都必须编写所有这些样板文件.别无选择.

现在让我们看看绑定方式:

return getCollection().bind({}).spread(function(col, done) {
    this.db = col;
    this.close = done;
    return this.db.query(stuff);
}).then(function() {
    return this.db.query(otherStuff);
}).finally(function() {
    this.close();
});
Run Code Online (Sandbox Code Playgroud)

然而,现在,这可以模块化.

var db = {
    getDb: function() { return getCollection().bind({}); },
    close: function() { this.close(); }
};

return db.getDb().then(function() {
    return this.db.query(stuff);
}).then(function() {
    return this.db.query(otherStuff);
}).finally(db.close);
Run Code Online (Sandbox Code Playgroud)

这已经好多了!但我们仍然需要考虑使用finally.

然后,蓝鸟引入的方式,Promise.using.通过这种方式声明:

function getDb() {
    var close;
    return getCollection().spread(function(db, done) {
        close = done;
        return db;
    }).disposer(function() {
        close();
    });
}
Run Code Online (Sandbox Code Playgroud)

您可以像以前一样使用它:

return Promise.using(getDb(), function(db) {
    return db.query(stuff).then(function() {
        return db.query(otherStuff);
    });
});
Run Code Online (Sandbox Code Playgroud)

无需考虑finally,也没有样板.