從地域到天堂:異步編程模式的革命性演變
作為計算機早期的一名軟件工程師,你面臨一個棘手的問題。
這一時期操作系統中已經發明了線程,線程非常好用,因此快速流行起來。
但在一些特定場景下線程也有自己的問題,尤其是后端服務器。
大家都會創建一個線程來處理用戶請求,如果在此期間發起一個阻塞調用(比如調用下游服務),那么整個線程都會被掛起,在此期間,線程不能執行任何其他工作:
get_response(); // 線程被阻塞
parse_response();在高并發場景下,這需要創建大量線程,導致巨大的線程創建/切換開銷和內存消耗。
這就是最開始的同步編程模型,它簡單、直觀,但并不高效。
回調函數與異步編程
為了解決同步編程模型低效的問題你發明了一個簡單而優雅概念:回調函數,callback。
線程不再原地阻塞等待操作完成,而是提供一個函數,告訴系統"當操作完成時,調用這個函數"。
get_response(parse_response); // get_response直接返回這里,parse_response就是回調函數,意思是得到下游的response后用parse_response來處理結果,這樣get_response會立刻返回不會阻塞線程,parse_response會在其它線程中被執行。
至此,你發明了異步編程。
利用異步編程你不必創建大量線程就能高并發處理請求。
但隨著項目復雜度增加,你開始遇到新的問題。
回調地獄:噩夢的開始
當你需要執行一系列依賴的異步操作時,回調函數開始變得難以管理。例如,你需要先獲取用戶信息,然后獲取用戶的訂單,最后獲取訂單的詳細信息:
getUser(userId, function(user) {
getUserOrders(user.id, function(orders) {
getOrderDetails(orders[0].id, function(details) {
displayOrderDetails(details);
}, function(error) {
handleOrderDetailsError(error);
});
}, function(error) {
handleOrdersError(error);
});
}, function(error) {
handleUserError(error);
});這種代碼結構被形象地稱為"回調地獄"(Callback Hell),回調函數本身會切割處理邏輯,而隨著嵌套層級的增加,代碼變得越來越難以閱讀和維護。
使用回調函數實現復雜的控制流(如并行執行多個異步操作,或者有條件地執行異步操作)非常困難,你需要新的異步編程范式。
經過反復思考,你意識到問題的核心在于:回調函數并沒有優雅的以線性方式組合異步操作。
該怎么解決這個問題呢?
未來還是現在?
很簡單,再來一層抽象,這種抽象需要創造一種“時間容器”,將“值”與“計算過程”分離,然后以鏈式調用的方式編排異步操作。
這種抽象就是promise/future。
Promise(或在某些語言中稱為Future)。這是一個代表"未來某個時刻會有結果"的對象,但是現在你可以基于這個對象進行各種操作, Promise最強大的特性是它支持鏈式調用,通過.then()方法,你可以指定當未來的結果到來時要執行的操作;通過.catch()方法,你可以處理Promise失敗的情況。
getUser(userId)
.then(user => getUserOrders(user.id))
.then(orders => getOrderDetails(orders[0].id))
.then(details => displayOrderDetails(details))
.catch(error => handleError(error));這種鏈式結構成功的實現了以線性方式組合異步操作,它沒有回調函數那樣的深度嵌套。
除了鏈式調用,Promise還提供了強大的組合功能,例如Promise.all()可以并行執行多個Promise,當所有Promise都成功時返回所有結果的數組。
這些組合方法讓你能夠輕松處理復雜的并發場景,這在回調模式中是極其困難的。
同步加異步
Promise徹底改變了你處理異步代碼的方式。代碼結構變得更加清晰,錯誤處理更加集中,控制流更加靈活。你終于擺脫了回調地獄的噩夢。
然而,隨著你使用Promise的時間增長,你開始注意到一個新的問題:雖然Promise解決了嵌套問題,但它仍然基于回調機制(.then()和.catch()方法本質上是注冊回調函數),對于特別復雜的異步邏輯,代碼仍然不夠直觀。
實際上,promise/future說到底只是一種偽同步代碼風格而已,開發者仍需在“回調式”思維和“偽同步”代碼之間轉換。
那么有可能把同步編程的直觀和異步編程的高效結合起來嗎?
你意識到要想用同步編程來實現異步編程的高效就不能在發起阻塞操作時真的阻塞線程。






















