JavaScript 的異步編程特性,既是福音,也可能是詛咒,因為它會導致“回調地獄”。雖然 Async.js 等實用程序庫可以幫助組織異步代碼,但有效跟踪控制流並推斷異步代碼的邏輯仍然很困難。
本文將介紹響應式編程的概念,它利用 Bacon.js 等庫來處理 JavaScript 的異步特性。
關鍵要點
開始響應式編程
響應式編程處理的是異步數據流。它用可觀察模式取代了迭代器模式。這與命令式編程不同,在命令式編程中,您主動迭代數據來處理事務。在響應式編程中,您訂閱數據並異步響應事件。
Bart De Smet 在他的演講中解釋了這種轉變。 André Staltz 在這篇文章中深入探討了響應式編程。
一旦您開始使用響應式編程,一切都會變成異步數據流:服務器上的數據庫、鼠標事件、Promise 和服務器請求。這使您可以避免所謂的“回調地獄”,並提供更好的錯誤處理。這種方法的另一個強大功能是能夠組合數據流,這為您提供了極大的控制和靈活性。 Jafar Husain 在他的演講中解釋了這些概念。
Bacon.js 是一個響應式編程庫,它是 RxJS 的替代方案。在接下來的部分中,我們將使用 Bacon.js 來構建一個廣為人知的“吃豆人”遊戲的版本。
項目設置
要安裝 Bacon.js,您可以使用 Bower,在 CLI 上運行以下命令:
$ bower install bacon
安裝庫後,您就可以開始使用響應式編程了。
為了外觀和感覺,我將使用基於文本的系統,這樣我就不必處理資源和精靈。為了避免自己創建,我將使用一個很棒的庫 UnicodeTiles.js。
首先,我創建了一個名為 PacmanGame 的類,它處理遊戲邏輯。它提供以下方法:
此外,它還公開了以下回調:
因此,要使用此 API,我們將啟動遊戲,定期調用 spawnGhost 來生成幽靈,偵聽 onPacmanMove 回調,並且每當發生這種情況時,調用 movePacman 來實際移動吃豆人。我們還定期調用 updateGhosts 來更新幽靈的移動。最後,我們定期調用 tick 來更新更改。重要的是,我們將使用 Bacon.js 來幫助我們處理事件。
在開始之前,讓我們創建遊戲對象:
$ bower install bacon
我們創建一個新的 PacmanGame,並傳入一個父 DOM 對象 parentDiv,遊戲將在此對像中渲染。現在我們準備構建我們的遊戲了。
事件流或可觀察對象
事件流 是一個可觀察對象,您可以訂閱它以異步觀察事件。您可以使用以下三種方法觀察這三種類型的事件:
既然我們已經了解了事件流的基本用法,那麼讓我們看看如何創建一個事件流。 Bacon.js 提供了幾種方法,您可以使用這些方法從 jQuery 事件、Ajax Promise、DOM EventTarget、簡單的回調甚至數組創建事件流。
關於事件流的另一個有用的概念是時間概念。也就是說,事件可能會在未來的某個時間到來。例如,這些方法創建在某個時間間隔內傳遞事件的事件流:
為了獲得更多控制,您可以使用 Bacon.fromBinder() 來創建自己的事件流。我們將在遊戲中通過創建一個 moveStream 變量來展示這一點,該變量將為我們的吃豆人移動生成事件。
var game = new PacmanGame(parentDiv);
我們可以使用一個值來調用 sink,該值將發送一個事件,觀察者可以偵聽該事件。對 sink 的調用在我們 onPacmanMove 回調中——也就是說,每當用戶按下按鍵請求吃豆人移動時。因此,我們創建了一個可觀察對象,它發出關於吃豆人移動請求的事件。
請注意,我們使用一個簡單值 moveV 調用了 sink。這將使用值 moveV 推送移動事件。我們還可以推送諸如 Bacon.Error 或 Bacon.End 之類的事件。
讓我們創建另一個事件流。這次我們想要發出通知以生成幽靈的事件。我們將為此創建一個 spawnStream 變量:
$ bower install bacon
Bacon.sequentially() 創建一個流,該流以給定的間隔傳遞值。在我們的例子中,它將每 800 毫秒傳遞一個幽靈顏色。我們還有一個對 delay() 方法的調用。它延遲了流,因此事件將在 2.5 秒的延遲後開始發出。
在本節中,我將列出一些更實用的方法,這些方法可用於事件流:
有關事件流的更多方法,請參閱官方文檔頁面。可以使用大理石圖查看節流和去抖動之間的區別:
var game = new PacmanGame(parentDiv);
如您所見,節流按慣例節流事件,而去抖動僅在給定的“靜默期”後才發出事件。
這些實用程序方法簡單而強大,能夠概念化和控制流,從而控制其中的數據。我建議觀看這段關於 Netflix 如何使用這些簡單方法創建自動完成框的演講。
到目前為止,我們已經創建和操作了事件流,現在我們將通過訂閱流來觀察事件。
回顧一下我們之前創建的 moveStream 和 spawnStream。現在讓我們訂閱它們:
var moveStream = Bacon.fromBinder(function(sink) { game.onPacmanMove = function(moveV) { sink(moveV); }; });
儘管您可以使用 stream.subscribe() 來訂閱流,但您也可以使用 stream.onValue()。不同之處在於,subscribe 將發出我們之前看到的這三種類型的事件,而 onValue 僅發出 Bacon.Next 類型的事件。也就是說,它將忽略 Bacon.Error 和 Bacon.End 事件。
當 spawnStream 上出現事件時(每 800 毫秒發生一次),其值將是幽靈顏色之一,我們使用該顏色來生成幽靈。當 moveStream 上出現事件時,請記住,當用戶按下按鍵來移動吃豆人時,就會發生這種情況。我們使用方向 moveV 調用 game.movePacman:它隨事件一起出現,因此吃豆人會移動。
您可以組合事件流以創建其他流。組合事件流的方法有很多,這裡列出其中幾種:
讓我們來看一個 Bacon.combineTemplate 的例子:
$ bower install bacon
如您所見,我們使用模板將事件流(即 password、username、firstname 和 lastname)組合成一個名為 loginInfo 的組合事件流。每當事件流接收到事件時,loginInfo 流都會發出事件,將所有其他模板組合成單個模闆對象。
Bacon.js 還有一種組合流的方法,即 Bacon.Bus()。 Bacon.Bus() 是一個事件流,允許您將值推入流中。它還允許將其他流插入 Bus 中。我們將使用它來構建遊戲的最後一部分:
var game = new PacmanGame(parentDiv);
現在我們使用 Bacon.interval 創建另一個流——ghostStream。此流將每 1 秒發出 0。這次我們訂閱它並調用 game.updateGhosts 來移動幽靈。這是為了每 1 秒移動一次幽靈。請注意已註釋掉 game.tick,並記住 moveStream 中的其他 game.tick?這兩個流都會更新遊戲,最後調用game.tick 來渲染更改,因此,與其在每個流中調用game.tick,我們可以生成第三個流——這兩個流的組合——並在組合流中調用game.tick。
為了組合流,我們可以使用 Bacon.Bus。這是我們遊戲中最終的事件流,我們稱之為 combinedTickStream。然後我們將 moveStream 和 ghostStream 插入其中,最後訂閱它並在其中調用 game.tick。
就是這樣,我們完成了。唯一剩下的就是使用 game.start(); 啟動遊戲。
Bacon.Property 和更多示例
Bacon.Property 是一種響應式屬性。想像一下,一個響應式屬性是一個數組的總和。當我們向數組添加一個元素時,響應式屬性將做出反應並自行更新。要使用 Bacon.Property,您可以訂閱它並偵聽更改,或者使用 property.assign(obj, method) 方法,該方法在屬性更改時調用給定對象的方法。這是一個如何使用 Bacon.Property 的示例:
var moveStream = Bacon.fromBinder(function(sink) { game.onPacmanMove = function(moveV) { sink(moveV); }; });
首先,我們創建一個事件流,該流以 1 秒的間隔生成給定數組的值——1、2、3 和 4,然後我們創建一個響應式屬性,它是掃描結果。這將為 reactiveValue 分配 1、3、6 和 10 的值。
了解更多信息和實時演示
在本文中,我們通過構建吃豆人遊戲介紹了使用 Bacon.js 進行響應式編程。它簡化了我們的遊戲設計,並通過事件流的概念為我們提供了更多控制和靈活性。完整的源代碼可在 GitHub 上找到,實時演示可在此處找到。
以下是一些更有用的鏈接:
要開始使用 Bacon.js 構建自己的吃豆人遊戲,您首先需要了解 JavaScript 和函數式響應式編程 (FRP) 的基礎知識。掌握這些知識後,您可以開始設置開發環境。您需要在計算機上安裝 Node.js 和 npm(Node 包管理器)。之後,您可以使用 npm 安裝 Bacon.js。設置好所有內容後,您可以開始編寫遊戲代碼。您可以按照我們網站上的教程獲取有關如何使用 Bacon.js 構建吃豆人遊戲的逐步指南。
Bacon.js 是一個用於 JavaScript 的函數式響應式編程 (FRP) 庫。它允許您以更易於管理和閱讀的方式處理異步事件,例如用戶輸入。在構建吃豆人遊戲的過程中,Bacon.js 可用於處理用戶輸入(例如鍵盤事件)、遊戲邏輯(例如吃豆人和幽靈的移動)以及將游戲狀態渲染到屏幕上。
當然可以!使用 Bacon.js 構建基本吃豆人遊戲後,您可以根據自己的喜好進行自定義。您可以更改遊戲的視覺效果、添加新功能,甚至修改遊戲的規則。可能性是無限的,最好的部分是,您可以在仍然受益於 Bacon.js 和函數式響應式編程的強大功能和簡單性的同時做到這一切。
調試使用 Bacon.js 構建的吃豆人遊戲類似於調試任何其他 JavaScript 應用程序。您可以使用瀏覽器的開發者工具來檢查代碼、設置斷點和單步執行代碼。此外,Bacon.js 提供了一種名為“onError”的方法,您可以使用它來處理事件流中的錯誤。
有幾種方法可以優化使用 Bacon.js 構建的吃豆人遊戲的性能。一種方法是最小化 DOM 更新的數量。您可以使用 Bacon.js 的“combineTemplate”函數將多個流組合成一個更新 DOM 的單個流來實現此目的。另一種方法是使用“flatMap”函數來避免創建不必要的流。
是的,您可以使用 Bacon.js 構建任何需要處理異步事件的遊戲類型。這不僅包括像吃豆人這樣的經典街機遊戲,還包括更複雜的遊戲,例如實時戰略遊戲或多人在線遊戲。
向使用 Bacon.js 構建的吃豆人遊戲中添加多人遊戲功能需要一個服務器來處理玩家之間的通信。您可以為此使用 Node.js 和 WebSockets。在客戶端,您將使用 Bacon.js 來處理傳入和傳出的 WebSocket 消息。
是的,您可以將使用 Bacon.js 構建的吃豆人遊戲部署到網站上。您需要使用 Webpack 或 Browserify 等工具捆綁您的 JavaScript 代碼,然後您可以將捆綁的代碼和遊戲的資源(例如圖像和聲音)託管在 Web 服務器上。
是的,您可以將 Bacon.js 與其他 JavaScript 庫或框架一起使用。 Bacon.js 是一個獨立的庫,因此它不依賴於其他庫或框架。但是,它可以與其他庫或框架結合使用以構建更複雜的應用程序。
網上有很多資源可以學習函數式響應式編程 (FRP) 和 Bacon.js。您可以從官方 Bacon.js 文檔開始,該文檔提供了對該庫的功能和 API 的全面指南。還有許多教程、博客文章和在線課程更詳細地介紹了 FRP 和 Bacon.js。
以上是用Bacon.js建立Pacman遊戲的詳細內容。更多資訊請關注PHP中文網其他相關文章!