2015-12-03 40 views
-2

當我使用異步模塊和bookshelf.js orm時,重複記錄(人員角色)正被添加到數據庫中,即使在添加它們之前檢查,我找不到原因。Node.js async order

簡而言之,這就是我試圖實現的目標。

  • 訪問某些URL(並行,這是一個網站刮板,它所訪問的每一次感謝http.globalAgent.maxSockets 10頁的URL),取person idperson nameroles
  • 如果一個人存在於數據庫中(我使用人員ID進行檢查,它與我從URL中獲取的ID相同),請添加他/她。如果記錄存在,不要做任何事情。
  • 獲取此人的角色名稱並查詢數據庫。如果角色不存在於數據庫中,請先將其添加到ID中,然後將其添加到數據透視表(多對多關係,因爲一個人可以有多個角色)如果角色存在,只需添加它與相關的person_id的數據透視表。

我使用asyncrequestcheeriobookshelf.js

app.get('/async', function (req, res) { 

    var urlArr = []; 
    for (var i = 10; i < 100; i++) { 
    urlArr.push("http://www.example.com/person/" + i + "/personname.html"); 
    } 
    async.each(urlArr, function (url, callback) { 
     request({ 
     url: url, 
     headers: { 
      'User-Agent': req.headers['user-agent'], 
      'Content-Type': 'application/json; charset=utf-8' 
     } 
     }, function (err, resp, body) { 
     if (err) { 
      console.log(err); 
     } else { 
      if (cheerio.load(body)) { 
      var $ = cheerio.load(body); 
      var links = $('#container'); 
      var name = links.find('span[itemprop="name"]').html(); // name 
      if (name == null) { 
       console.log("null returned, do nothing"); 
      } else { 
       name = entities.decodeHTML(name); 
       var r = url.substring(33, 35); 
       person.where('id', r).fetch({require: true}).then(function (p) { 
       // person exists, don't do anything 
       }).catch(function() { 
       // person doesn't exist, proceed 
       var numberOfRoles = links.find('span[itemprop="roletitle"]').length; // number of roles 
       new person({id: r, name: name}).save(null, {method: 'insert'}).then(function (returnval) { 
        var rolesArr = []; 
        for (var b = 0; b < numberOfRoles; b++) { 
        rolesArr.push(b); 
        } 
        async.each(rolesArr, function (roles, callback) { 
        var personTypes = $('span[itemprop="roletitle"]').eq(roles).text(); 
        var personTypes = entities.decodeHTML(personTypes); 

        personRole.where('role', personTypes).fetch().then(function (data1) { 
         // role exists, add the role_id and person_id to the pivot table 
         new personPersonRole({ 
         person_id: r, 
         personrole_id: data1.id 
         }).save().then(function (data2) { 
         console.log("role has been added"); 
         }); 
        }).catch(function() { 
         // role doesn't exist, add it first 
         new personRole({ 
         role: personTypes 
         }).save().then(function (data3) { 
         console.log("new added role_id is : " + data3.id); 
         // add person_id and role_id to the pivot table 
         new personPersonRole({ 
          person_id: r, 
          personrole_id: data3.id 
         }).save(); 
         }); 
        }); 
        callback(); 
        }); 
       }); 
       }); 
      } 
      } 
      else { 
      console.log("can't open"); 
      } 
     } 
     }); 
     callback(); 
    }, 
    function (err) { 
     if (err) { 
     console.log("err"); 
     } else { 
     console.log("success"); 
     } 
    }); 
}); 

回答

0

它看起來像是多線程或異步編程中的常見問題。什麼是可能發生的事情是這樣的:

  • 刮板被告知要刮URL一個
  • 刮板被告知要刮網址B.
  • ...
  • 要求從URL角色X是否存在
  • 詢問URL B中的角色X是否存在
  • 時間流逝; MySQL確定X不存在,觸發URL回調處理程序
  • 時間流逝; MySQL確定X不再存在(再次),觸發對URL B處理程序的回調
  • URL處理程序的回調會從事件隊列中彈出;告訴MySQL爲URL A創建角色X
  • URL B處理程序的回調會從事件隊列中彈出;告訴MySQL爲URL B創建角色X

你需要做的事很簡單:在你的數據庫中創建角色名稱UNIQUE。這不會阻止代碼嘗試創建角色兩次,但每次只會失敗一次。那麼不要檢查角色是否存在;只是嘗試每次創建它。

另外,還有一個clever trick with SQL,您可以在其中插入一些東西,如果它不存在。它可能會或可能不會與書架一起工作,但我不夠勇於嘗試。