2017-04-19 113 views
1

我需要實現系統我應該如何在使用DB + HTTP調用承諾節點JS

  • 從父集合中獲取數據。
  • 檢查在Redis的
  • 發現,如果沒有那麼做一個HTTP調用,並得到JSON數據然後設置緩存
  • 如果是,那麼從緩存中
  • 將數據存入孩子收集父ID獲取數據尤其關鍵。

我有工作解決方案使用回調類似的東西。

MongoClient.connect(dsn).then(function(db) { 
    parentcollection.findOne({"_id" : new ObjectId(pid)}, function(err, data) { 

    var redis = require("redis"), 
    client = redis.createClient(); 

    client.on("error", function (err) { 
     console.log("Error " + err); 
    }); 

    // If not set 

    client.get(cacheKey, function(err, data) { 
     // data is null if the key doesn't exist 
     if(err || data === null) { 
      var options = { 
       host: HOST, 
       port: 80, 
       path: URI 
      }; 

      var req = http.get(options, function(res) { 
       res.setEncoding('utf8'); 
       res.on('data', function (chunk) { 
        body += chunk; 
        //console.log('CHUNK: ' + chunk); 
       }); 
       res.on('end', function() { 
        data = JSON.parse(body); 


        // Get childdata After process of data 

        childcollection.save(childdata, {w:1}, function(cerr, inserted) { 
         db.close(); 

        }); 
       }); 
      }); 
     } else { 
      // Get childdata from cache data 
      childcollection.save(childdata, {w:1}, function(cerr, inserted) { 
       db.close(); 
      }); 
     } 
    }); 
}); 

我想使用承諾(本機,而不是外部的像藍鳥/請求),而不是回調。我籤手冊和思考,如果我需要實現這樣的

var promise1 = new Promise((resolve, reject) => { 

    MongoClient.connect(dsn).then(function(db) { 
     parentcollection.findOne({"_id" : new ObjectId(pid)}, function(err, data) { 


    }); 

}}.then(function(data){ 
    var promise2 = new Promise((resolve, reject) => { 
    var redis = require("redis"), 
     client = redis.createClient(); 

     client.on("error", function (err) { 
      console.log("Error " + err); 
     }); 

     // If not set 

     client.get(cacheKey, function(err, data) { 
      // data is null if the key doesn't exist 
      if(err || data === null) { 
       var options = { 
        host: HOST, 
        port: 80, 
        path: URI 
       }; 

       var promise3 = new Promise((resolve, reject) => { 
         var req = http.get(options, function(res) { 
          res.setEncoding('utf8'); 
          res.on('data', function (chunk) { 
           body += chunk; 
           //console.log('CHUNK: ' + chunk); 
          }); 
          res.on('end', function() { 
           data = JSON.parse(body); 


          // Get childdata After process of data 


         }); 
        }) 
       }).then(function(data){ 
        childcollection.save(childdata, {w:1}, function(cerr, inserted) { 
           db.close(); 

          }); 
       }); 
      } else { 
       // Get childdata from cache data 
       childcollection.save(childdata, {w:1}, function(cerr, inserted) { 
        db.close(); 
       }); 
      } 
     }); 

}}.then(function(data){ 
}); 
});    

看起來像髒回調地獄或任何更好的辦法,不使用的承諾像上面?

+0

因素了'新Promise'調用到額外的承諾,回國功能。 – Bergi

回答

1

一個問題是,你永遠不會打電話解決函數提供給承諾構造函數回調。沒有給他們打電話,承諾永遠不會解決。

我會建議在單獨的,可重用的函數中創建這些新的承諾。另一方面,當你不提供回調參數時,一些MongoDb方法已經返回promise。

你可以像下面這樣做。

// Two promisifying functions: 
function promiseClientData(client, key) { 
    return new Promise(function (resolve, reject) { 
     return client.get(key, function (err, data) { 
      return err ? reject(err) : resolve(data); // fulfull the promise 
     }); 
    }); 
} 

function promiseHttpData(options) { 
    return new Promise(function (resolve, reject) { 
     return http.get(options, function(res) { 
      var body = ''; // You need to initialise this... 
      res.setEncoding('utf8'); 
      res.on('data', function (chunk) { 
       body += chunk; 
       //console.log('CHUNK: ' + chunk); 
      }); 
      res.on('end', function() { 
       data = JSON.parse(body); 
       resolve(data); // fulfull the promise 
      }); 
     ); 
    }); 
} 

// Declare the db variable outside of the promise chain to avoid 
// having to pass it through 
var db; 

// The actual promise chain: 
MongoClient.connect(dsn).then(function (dbArg) { 
    db = dbArg; 
    return parentcollection.findOne({"_id" : new ObjectId(pid)}); // returns a promise 
}).then(function (data) { 
    var redis = require("redis"), 
    client = redis.createClient(); 
    client.on("error", function (err) { 
     console.log("Error " + err); 
    }); 
    // Get somehow cacheKey... 
    // ... 
    return promiseClientData(client, cacheKey); 
}).then(function (data) { 
    // If not set: data is null if the key doesn't exist 
    // Throwing an error will trigger the next `catch` callback 
    if(data === null) throw "key does not exist"; 
    return data; 
}).catch(function (err) { 
    var options = { 
     host: HOST, 
     port: 80, 
     path: URI 
    }; 
    return promiseHttpData(options); 
}).then(function (data) { 
    // Get childdata by processing data (in either case) 
    // .... 
    // .... 
    return childcollection.save(childdata, {w:1}); // returns a promise 
}).then(function() { 
    db.close(); 
}); 

我認爲MongoDb返回的承諾完全符合。如有疑問,您可以撥打他們Promise.resolve()把它們變成本地JavaScript的承諾,比如像這樣:

return Promise.resolve(parentcollection.findOne({"_id" : new ObjectId(pid)})); 

或:

return Promise.resolve(childcollection.save(childdata, {w:1})); 
+0

感謝您的示例代碼幫助我通過承諾編寫代碼,但是我不使用throw/catch作爲redis的條件流,但使用類似於http://stackoverflow.com/a/33260670/1230744 –

+0

的功能流當然,這將工作。確保避免代碼重複:'childcollection.save'應該只發生一次。此外,如果您不使用catch,請注意,如果發生異常,則鏈條被破壞,錯誤不會再被捕獲。投擲/捕捉在承諾鏈中非常有效。 – trincot

+0

是的,我使用捕獲承諾鏈中的所有通用失敗,這是我猶豫是否使用catch作爲正常事件的redis關鍵失敗的主要原因。 –