2015-10-10 59 views
0

我开始使用承诺并尝试使用它们而不是回调来避免回调地狱。异步函数是从MongoDB的,Redis的,bcrypt模块等功能的组合,我能够远远地得到这样的:承诺基本知识。如何promisify异步节点代码?

var insert = Q.denodify(db.collection(USERS).insert); 
var createCollection = Q.denodify(db.createCollection); 
var sadd = Q.denodify(redisClient.sadd); 
var sismember = Q.denodify(redisClient.sismember); 
var genSalt = Q.denodify(bcrypt.genSalt); 
var hash = Q.denodify(bcrypt.hash); 

// SANITY CHECK 
// a "name" parameter is required 
if(!req.body.name || !isValidName(req.body.name)) 
    return next(get400InvalidNameError()); 

// SANITY CHECK 
// a "key" is optional 
// if one is supplied, it must be valid 
// this key will be hashed later 
if(req.body.key){ 
    if(!isValidKey(req.body.key)) 
     return next(get400InvalidKeyError()); 
} 

// STEPS: 
// 1. 
// check Redis cache to see if the "name" is already taken 
// if yes, return error. if no, continue 
// 2. 
// construct a "user" object with name = req.body.name 
// 3. 
// req.body.key provided? 
// if yes, generate salt and hash the key. Set user.key = hash 
// if not, continue 
// 4. 
// create a collection in MongoDB with the same name as req.body.name 
// 5. 
// add req.body.name to cache 
// 6. 
// send a response to user using res.json()/res.end() 
sismember(USERS,req.body.name) 
.then(createUserObj,errHandler) 
.then(genSalt(10),errHandler) 
.then(hash(req.body.key,salt)) 
.then(createCollection(req.body.name),errHandler) 
.then(sadd(USERS,req.body.name),errHandler) 
.then(insert(user),errHandler) 
.then(get200UserCreated,errHandler) 

什么混淆我是所有这些功能都then() -ed在一起的最后一部分。我有几个问题:
1.如何使一个异步函数的结果可用于另一个?
2.如何有条件地决定执行哪些功能?例如,我只想在提供req.body.key时生成salt和hash。
3.我的then()序列是否正确?

+0

'1.'这是如何工作的承诺解决的承诺的结果成为下一个的参数 – Anonymous0day

+0

问题1和2的答案可以很容易地找到StackOverflow,如果你花一点时间看看。至于3,不,因为你在定义'then'参数时立即调用函数,而不是定义稍后调用的函数。 – Touffy

回答

3
  1. 如何一个异步函数的结果提供给别人呢?

当您使用Promise时,您可以解决或拒绝它。

var myprom = Promise.resolve(1) // the simplest promise way 

在这里,如果我们有一个then链中的参数将等于1

myprom.then(function(val){ 

    console.log(val); // 1 
    return 33; 

}).then(function(passed){ 

    console.log(passed) // 33 
    return 44; 

}).then(function(other){ 

    console.log(other) // 44 
    if(other > 44){ 
     return 'AAA'; 
    } else { 
     return 'BBB'; 
    } 
}).then(function(res){ 

    console.log(res) // BBB 

}) 

这里重要的是,你从你的诺言return东西。
现在是异步。部分:

// we can also instanciate a new Promise object 
var myprom = new Promise(function(resolve , reject){ 

    console.log('we start here'); 

    setTimeout(function(){ 
     console.log('we continue here'); 
     resolve('time out'); 
    } , 2000); 

}).then(function(val){ 
    console.log('and we end here : ' + val); 
}); 

第二部分then是由resolve在第一部分中的呼叫调用。
所以我们总是等到最后,这是承诺的“魔力”。

通过解析的参数成为下一个then的参数。
这是相同的作为我们的第一个例子对return这是非常重要的

当你这样做:

sismember(USERS,req.body.name) 

它的原理是一样的超时。

var sismember = function(USERS , req.body.name){ 
    var promiseToReturn = new Promise(function(resolve,reject){ 
     var result = ''; // <---do something with USERS and req.body.name 

     // and if it is ok 
      if(result){ 
      resolve(result) 
      } else {// if it is not ok 
      reject(result) 
      } 

    }); 
    return promiseToReturn; // <-- we can chain with then 
}) 
.then(createUserObj,errHandler) 

下一然后将(sismember之后)将调用createUserObjresult作为参数。
它变成:

var sismember = function(USERS , req.body.name){ 
    var promiseToReturn = new Promise(function(resolve,reject){ 
     var result = ''; // <---do something with USERS and req.body.name 

     // and if it is ok 
      if(result){ 
      resolve(result) 
      } else {// if it is not ok 
      reject(result) 
      } 

    }); 
    return promiseToReturn; // <-- we can chain with then 
}) 
.then(function createUserObj(resultOfsismember){ 
     var createdUser = {} //<--- do what you want with the resultOfsismember 
     // and return something to be handled by next then. 
    if(everyThingIsOk){ 
     return createdUser; 
    } else { 
     return Promise.reject('error during creation'); 
     // this will be handled by next then error handler or catch 
    } 
} , errHandler); //<--- you provide here an error handler for sismember 
  • 我如何可以有条件地决定哪些功能得到执行?例如,我只想在提供req.body.key时生成salt和hash。
  • 不同的方法来做到这一点。

  • 是我然后()序列正确?
  • 不!

    你不能像你那样调用函数。

    如果你想添加的参数使用 bind

    .then(genSalt.bind(null , 10), errHandler) 
    // we invoke the genSalt in a null context with 10 as first argument 
    

    但这个承诺链提供从上一个函数的参数

    .then(genSalt(10),errHandler) // <-- you are invoking the result not the function 
    // you are doing something like that: 
    .then('Adekj34LLKDF' , errHandler) 
    

    你没有提供它!
    见上面的例子


    一旦你解决了这个问题,在末尾添加一个catch来处理可能发生的任何错误。 这一部分:

    .then(get200UserCreated,errHandler)//<--- errHandler for prev then 
    

    有错误处理程序,但仅适用于本部分:

    .then(insert(user),errHandler) 
    

    如果您在insert(user)有错误,否则将在下然后错误处理程序或挂钩处理。

    sismember(USERS,req.body.name) 
    .then(createUserObj,errHandler)// <-- errHandler for : sismember 
    .then(genSalt(10),errHandler)// <-- errHandler for : createUserObj 
    .then(hash(req.body.key,salt))// <-- missing errHandler for : genSalt 
    .then(createCollection(req.body.name),errHandler)// <-- errHandler for : hash 
    .then(sadd(USERS,req.body.name),errHandler)// <-- errHandler for createCollection 
    .then(insert(user),errHandler)// <-- errHandler for : 
    .then(get200UserCreated,errHandler)// <-- errHandler for : insert 
    .catch(errHandler)// <-- errHandler for : get200UserCreated 
    

    我已经写了一些关于错误处理程序here,你应该看一看这一点。