2015-04-03 54 views
0

如何讓鏈式函數在它之前等待函數執行正確?讓鏈式函數等待對方執行

我從模塊此摘錄:

var ParentFunction = function(){ 
    this.userAgent = "SomeAgent"; 
    return this; 
} 

ParentFunction.prototype.login = function(){ 
    var _this = this; 

    request.post(
     url, { 
     "headers": { 
      "User-Agent": _this.userAgent 
     } 
    }, function(err, response, body){ 
     return _this; 
    }) 
} 

ParentFunction.prototype.user = function(username){ 
    this.username = username; 
    return this; 
} 

ParentFunction.prototype.exec = function(callback){ 
    request.post(
     anotherURL, { 
     "headers": { 
      "User-Agent": _this.userAgent 
     } 
    }, function(err, response, body){ 
     callback(body); 
    }) 
} 

module.exports = parentFunction; 

這是從我的服務器中:

var pF = require("./myModule.js"), 
    parentFunction = new pF(); 

parentFunction.login().user("Mobilpadde").exec(function(data){ 
    res.json(data); 
}); 

的問題是,該user -function不會等待login到完成(意思是,它在登錄之前執行返回_this)。那麼我該如何讓它等待呢?

回答

1

在執行鏈中的下一個呼叫之前,您無法使Javascript「等待」。整個鏈將按順序立即執行,無需等待任何異步操作完成。

我能想到使這個結構工作的唯一方法是創建一個等待執行的事件隊列,然後以某種方式監視該隊列中異步的事情,以便知道何時執行隊列中的下一個事件。這就要求每種方法都遵循某種標準約定,以便了解異步操作完成時該方法是否異步以及異步操作是否異步,以及鏈中是否存在錯誤時該如何操作。

jQuery爲jQuery動畫做了這樣的事情(它爲所有鏈接的動畫實現一個隊列,並在前一個動畫完成時依次調用每個動畫)。但是,它的實現比你在這裏更簡單,因爲它僅適用於jQuery動畫(或遵循適當約定的手動排隊函數),而不適用於其他jQuery方法。你提出了三種不同的方法,其中兩種甚至不是異步的。

因此,如果你想讓所有的方法遵循一系列的約定,那麼你所要求的可能會做得更多,但這並不容易。由於您似乎只有一個異步操作,所以我想知道您是否可以這樣做。您不會顯示.exec()方法的功能,但如果它只是在鏈的末尾調用某個其他函數,那麼您在鏈中只有一個異步方法,因此您可以讓它進行回調並做到這一點:

parentFunction.user("Mobilepadde").login(function(data) { 
    res.json(data); 
}); 

我正在這樣做的排隊手段,但它不是我可以編寫和測試,在不到一個小時,你就必須提供什麼樣的一些想法你想要處理鏈中任何地方發生的錯誤。非異步錯誤可能會引發異常,但異步操作完成後發生的異步錯誤甚至非異步錯誤不能只是拋出,因爲沒有好的方法來捕獲它們。所以,錯誤處理變得很複雜,你真的不應該着手一個不能預期適當的錯誤處理的設計路徑。

我越想到這個,我想你或者想要得到一個旨在處理異步操作(如異步模塊)的排序的庫,並將其用於排隊操作,或者您只想要放棄鏈接並使用承諾來支持操作的排序。當我考慮如何在異步操作的任務隊列中進行錯誤處理時,我發現所有這些問題都已經在promise中處理過(通過拒絕傳播錯誤並捕獲異步處理程序中的異常承諾拒絕等)。對異步操作進行錯誤處理是很困難的,也是構建排序異步操作承諾的一個重要原因。


所以,現在想解決這個使用的承諾,你可以做的就是使每一個異步方法返回一個承諾(你可以用一個調用了許多承諾庫,如藍鳥promisfy整個請求模塊)。然後,一個.login().exec()回報的承諾,你可以這樣做:

var Promise = require('bluebird'); 
var request = Promise.promisifyAll(require('request')); 

ParentFunction.prototype.login = function(){ 
    return request.postAsync(
     url, { 
     "headers": { 
      "User-Agent": this.userAgent 
     } 
    }); 
} 

ParentFunction.prototype.exec = function(){ 
    return request.postAsync(
     anotherURL, { 
     "headers": { 
      "User-Agent": this.userAgent 
     } 
    }).spread(function(response, body) { 
     return body; 
    }) 
} 

parentFunction.login().then(function() { 
    parentFunction.user("Mobilpadde"); 
    return parentFunction.exec().then(function(data) { 
     res.json(data); 
    }); 
}).catch(function(err) { 
    // handle errors here 
}); 

這不是鏈接,但它可以讓你在幾分鐘內去,而不是東西,可能需要很長一段時間寫的(有可靠的錯誤處理) 。

+0

哦,dangit!我真的希望能夠避免將對方的功能堆疊在一起。無論如何,我只是用缺少的exec函數更新了我的問題(並不是說它以任何方式改變了我的問題,但仍然如此)。 – Mobilpadde 2015-04-03 03:02:05

+0

@Mobilpadde - 查看我添加到我的答案的評論。我開始設計一個隊列的路徑,這個隊列可以嵌入到你的對象中以進行鏈接工作,但是混合同步和異步操作的組合,然後在異步操作完成之後嘗試找出適當的錯誤處理策略,問題變成一個相當大的問題。我建議採用不同的方法。如果所有操作都是同步的,則鏈接很容易。異步操作的混合使事情變得複雜。 – jfriend00 2015-04-03 03:15:51

+0

@Mobilpadde - 我添加了一個承諾風格的實現。 – jfriend00 2015-04-03 03:26:13

0

試試這個,看看它是否工作:

ParentFunction.prototype.login = function(callback){ 
    var _this = this; 
     request.post(
      url, { 
      "headers": { 
       "User-Agent": _this.userAgent 
      } 
     }, function(err, response, body){ 
      return callback(_this); 
     }) 
    } 
} 

在服務器端:

parentFunction.login(function(loggedin){ 
    loggedin.user("Mobilpadde").exec(function(data){ 
     res.json(data); 
    }); 
}); 
+0

是的,我知道這是可以做到的,但我真的寧願不這樣做,因爲我試圖瞭解鏈接。對不起,我已經在我的問題中說過了。 – Mobilpadde 2015-04-03 02:57:35