承诺基础知识.如何承诺异步节点代码

Promises basics. How to promisify async node code?

本文关键字:承诺 异步 节点 代码 基础知识 何承诺      更新时间:2023-09-26

我正在开始使用承诺并尝试使用它们而不是回调以避免回调地狱。异步函数是MongoDB,Redis,bcrypt模块等函数的混合。我能够走到这一步:

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()在一起的最后一部分。我有几个问题:
1. 一个异步函数的结果如何提供给另一个异步函数?
2. 如何有条件地决定执行哪些功能?例如,我想仅在提供req.body.key时才生成盐和哈希。
3. 我的then()顺序是否正确?

  1. 如何将一个异步函数的结果提供给另一个异步函数?

使用"承诺"时,可以解决或拒绝它。

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了一些东西.
现在是异步。.part:

// 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 之后(将使用 result 作为参数调用createUserObj.
它变成:

 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
  1. 如何有条件地决定执行哪些函数?例如,我想仅在提供 req.body.key 时才生成盐和哈希。

不同的方法。

  1. 我的 then(( 序列正确吗?

不!

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

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

如果要添加参数,请使用bind

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

但是 promise 链提供了来自 prev 函数的参数,您不必提供它!
见上例

<小时 />

解决此问题后,请在末尾添加一个 catch 以处理可能发生的任何错误。这部分:

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

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

.then(insert(user),errHandler)

如果您在insert(user)期间遇到错误,它将由下一个然后错误处理程序或 catch 处理。

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 

我在这里写了一些关于错误处理程序的东西,你应该看看。