摘要:應用需要正確并合適響應各種網絡請求用戶操作計時事件和各種延時動作。好了,我們的新流程圖畫出來了提示城市名稱查找不是很復雜,谷歌地圖為此提供了非常簡單的。形象點表示就是,函數是一等公民。
原文
當你將關注點從樣式,美學和網格系統轉移到邏輯,框架和編寫JavaScript代碼時。一切都開始了,你會發現你處于你的web開發歷程中最激動人心的那一刻。
在這個非常時刻你會發現,當涉及到JS時,它不僅僅是幾個簡單的jQuery技巧和視覺效果。你的視野是一整個web應用,而不再僅僅是局限于頁面。
當你把更多的精力投入到寫js代碼時,你會開始考慮交互、你的子模塊和邏輯。事情開始奏效,你感覺到你的app有了生命。一個全新的、令人興奮的世界出現在你眼前,同樣,也出現了很多全新的、棘手的問題。
你并不氣餒,并想出來各種各樣的辦法,代碼也寫的越來越多。嘗試某些博客文章中那各種各樣的技術,不斷地完善自己解決問題的方法。
然后,你開始覺得有些不對路。
你的腳本文件慢慢變大,一小時前才200行的,現在已經500行了。“嘿”——你想——“這沒什么大不了的”。隨后,你開始閱讀關于代碼維護的相關文章,并著手實現它。開始分離你的邏輯代碼,并把它們分塊、組件。事情開始又變好了點。代碼像圖書館藏書那樣分類存放。你感覺良好,因為各種各樣的文件被以正確的命名放置在合適的目錄里。代碼變得模塊化,更易于維護了。
然而,你又感覺不對路了,但是不知道哪里有問題。
web應用的行為很少是線性的。事實上,web應用的許多行為應該是瞬時發生(有時候應該是出乎意料或是自發地)。
應用需要正確并合適響應各種網絡請求、用戶操作、計時事件和各種延時動作。名為“異步”和“race condition”的怪物無時不刻在敲你的腦門。
你需要將你帥氣的模塊化結構與丑陋的新娘結合 - 異步代碼。一個棘手的問題來了:我應該把這段代碼放在哪里?
你會把你的app精心地劃分成一個個構建塊。導航和內容組件被整齊地放置在合適的目錄中,較小的輔助腳本文件包含了執行普通任務的重復代碼。一切都通過app.js這個文件來調度,一切都從這里開始。完美。
但是,你的目標是在app中的某個地方調用異步代碼,運行后把它放在一旁。
異步代碼應該放在ui組件么?或者放在主文件里?app的哪個構建塊負責響應呢?哪一個構建塊負責開始運行?錯誤處理呢?你在腦海里考慮著各種方法——但是你還是愁眉不解——你意識到如果想要拓展或維護這些代碼,那難度是相當大的,問題還沒解決。你需要理想的一勞永逸的方案。
放松一下,這對你來說沒有問題。事實上,你的思維越有條理,這種煩惱就會越強烈。
你開始閱讀有關處理此問題的信息并尋求即用型解決方案。一開始,你了解到promises優于回調的地方。隨后,你開始試圖了解什么是RxJS(并且為什么網上的一些人說這是解決網絡異步請求的唯一解決方案)。經過一些閱讀之后,你試著去理解,為什么一個博客寫道沒有redux-thunk的redux沒有意義,但是另一個人認為redux-saga也是如此。
一天結束后,你疲憊的大腦充斥著各種詞。閱讀完大量可行的方法后,你的想法噴涌出來。為什么會有這么多呢?那么復雜?人們怎么喜歡在互聯網上爭論,不去開發一個好的模式?
因為這些都不重要
無論使用哪種框架,異步代碼都不可能被正確地存放好。并沒有一個單一、通用、既定的解決方案,要根據具體的開發環境、需求來采取不同的方案。
并且,這篇文章也不會提供解決所有問題的方案。但是它可以給你提供一個好的思路,讓你處理好你的異步代碼——因為它都基于一個非常基本的原則。
從某些角度來看,編程語言的結構并不復雜。畢竟,它們只是類似于計算機的愚蠢東西,能夠在各種盒子里儲存值而已,并且通過if或函數調用改變程序執行流程。作為一種命令式和略微面向對象的語言,js在這里也是類似的。
這意味著究其本質,來自各路大神寫的各種宇宙級異步庫(無論是redux-saga、RxJS、觀察者或者其他奇奇怪怪的庫)都依賴相同的基本原理。它們并沒有那么神奇——它必須讓大家學習它的概念,這里并沒有新發明。
為什么這個事實如此重要?讓我們來考慮這樣的一個例子。
先來個簡單的app,這個app可以讓我們在地圖上標記我們喜歡的地方。沒有什么花哨的東西:只是右側的地圖視圖和左側的簡單側邊欄。單擊地圖應在地圖上保存新標記。
當然,我們需要一個與眾不同的特性:我們需要它用local storage記住我們標記好的地方列表。
綜上所述,我們可以畫一個流程圖出來
看,并不是很復雜
為簡潔起見,下面的示例將在不使用任何框架或UI庫的情況下編寫 - 僅涉及vanilla js。此外,我們將使用谷歌地圖API的一小部分 - 如果你想自己創建類似的應用程序,你應該注冊你的API密鑰[https://cloud.google.com/maps...](https://cloud.google.com/maps...
).
快速分析一下
init方法用google地圖api初始化地圖組件,注冊地圖點擊事件并且嘗試從local storage加載數據。
addPlace方法處理地圖點擊事件——把新地點加在列表里并且更新ui
renderMarkers方法迭代地點列表,清除地圖后,將標記放在其上。
忽略一些不完善的地方(沒有錯誤處理之類的)—— 它將作為原型提供足夠好的服務。完美。讓我們寫一些html:
假設我們寫了一些樣式(我們不會在這里介紹它,因為它不相關),不管你信不信 - 它實際上是這樣做的:
盡管它很丑,但是管用。不過可拓展性不好。
首先,我們的代碼責任分割不明確。如果你聽說過SOLID)原則,你應該清楚我們已經打破了第一條規則:單一責任原理。在我們的例子中——盡管很簡單——一個js文件包含了所有,包括處理用戶響應的代碼和數據轉換和異步代碼。“為什么這樣不好,運行起來不是棒棒的么?”——你可能會這么說。確實運行起來棒棒的,但是如果要加新特性那就不棒棒了——可維護性低。
我用一個例子讓你徹底心服口服:
首先,我們想要側邊欄加標記列表。第二,我們想要用googleAPI實現在地圖上看到城市名的功能——這就引入了異步代碼。
好了,我們的新流程圖畫出來了:
既然你調用別人的接口,那肯定不是同步代碼而是異步代碼啦。它首先要調用google的js庫,并且回復過來需要一定時間。雖然有點復雜,但是用于教學剛剛好。
讓我們回到ui代碼這里并且這里有個明顯的事實。我們的頁面分兩大塊,側邊欄和主要內容區。我們絕對不能把它們兩的代碼放在一起。原因很明顯——我們將來有四個組件怎么辦?六個呢?一百個呢?我們需要把我們的代碼分開——我們需要有兩個獨立的js文件。一個是側邊欄,一個是主要內容區塊。問題來了,哪一個應該存放地方標記列表的數組呢?
哪一個正確呢?哪個都不對。還記得單一責任原則么?為了降低代碼冗余度,我們應該以某種方式分離關注點并將我們的數據邏輯保存在其他地方。看吧:
代碼分離萬金油:我們可以把進行數據操作的代碼放到另一個文件里,這個文件集中處理數據。這個servce文件將負責那些與本地存儲同步的問題和機制。相反,組件將僅僅提供接口。這符合SOLID原則。讓我們介紹下這個模式:
Service code Map component code Sidebar component code:好了,一個大問題已經解決。代碼整齊擺放在它們該待的位置。但在我們感覺良好之前,運行下這個。
。。。oops。
在做任何動作之后,app沒有交互了。
為什么? 好吧,我們沒有實現任何同步手段。使用導入的方法添加地點后,我們不會在任何地方發出任何信號。在調用addPlace()之后,我們甚至無法在下一步調用getPlaces()方法,因為城市查找是異步的,需要時間來完成。
程序在后臺進行,但是并沒有反應到界面上——在地圖上添加標記后,我們沒有看到側邊欄的更新。怎么解決?
一個簡單的方法就是,使用定時器輪詢我們的服務,例如:
它有用么?emm。。有,但不是最佳方案。大多數情況下我們并不需要這個服務。
畢竟,你也不會定時去看你的包裹有沒到達。同樣地,如果你把汽車丟去維修,你也不會每半小時給修車師傅打電話詢問工作是否完成(至少希望你不是這種人)。正常的情況應該是這樣的,修車師傅修好了,自然會打電話給你。當然,我們事先留電話了。
現在,我們在js中嘗試下這種“留電話”的方式。
js是一門非常神奇的語言——它的一個古怪的特征就是可以把函數視為其他值。形象點表示就是,“函數是一等公民”。這意味著任何函數都可以分配給變量或作為參數傳遞給另一個函數。事實上你已經接觸過了:還記得setTimeout,setInterval和各種事件監聽器回調嗎? 它們通過將函數作為參數來使用。
這種特性在異步場景中是基礎我們可以定義一個更新我們的UI的函數 - 然后將它傳遞給另一部分的代碼,在那里它將被調用。
使用這種機制,我們可以將renderCities方法以某種方式傳遞給dataService。在那里,它將在必要時被調用:畢竟,服務能準確地知道何時應該將數據傳輸到組件。
試一試,我們首先在服務端添加這個功能,然后在某個時刻調用它。
現在,在sidebar那里使用
你知道會發生什么么?當在加載我們的sidebar代碼時,它在dataService注冊了renderCities方法。
在這種情況下,當我們的數據發生更改時,dataService就會調用此函數(由于addPlace()的調用)。
確切地說,我們的代碼的一部分是事件的SUBSCRIBER,另一部分是PUBLISHER(服務方法)。我們已經實現了發布 - 訂閱模式的最基本形式,這是幾乎所有高級異步概念的基本概念。
還有呢?
請注意,我們的代碼,僅限于一個監聽組件(即,一位訂閱者)。如果其他方法也用了這個subscribe方法來傳遞的話,它會覆蓋掉dataService的changeListener變量,為了解決這個問題,我們需要用數組來存儲監聽者。
現在,我們可以稍微整理一下代碼并編寫一個函數來為我們調用所有的監聽者:
這樣我們也可以連接map.js組件,以便它對服務中的所有操作做出正確的反應:
如果需要傳遞參數怎么辦?我們可以使用監聽者的參數直接獲得。像這樣:
然后,可以輕松地在組件中檢索數據:
這里還有更多的可能性 - 我們可以為不同類型的行為創建不同的主題(或渠道)。此外,我們可以提取發布和訂閱方法到一個文件并從那里使用它。但就目前而言,還OK啦 - 以下是使用我們剛剛創建的相同代碼的應用的簡短視頻
(譯者注,大家去原文那里看吧)
(譯者注:接下來的內容是作者關于這個模式的想法,他說,那些組件的概念比如RxjS,雖然它們功能更強大、概念更加地復雜,但是基本概念都是上文講過的。它們搞得太復雜了而已。并且這個模式也可以套在其他的地方。如DOM操作。另外,本文只是講了最基本的,還有很多地方可以拓展。比如取消訂閱、事件訂閱等等。最后作者還建議我們多點搞優秀的源代碼,down下來用debugger研究源碼。挖掘出它們最基本的思想。多動手、多思考,不要害怕專有名詞,覺得很高大上、很難理解。其實就是那么一回事。有些人搞得太復雜了。)
(譯者為什么不翻譯完呢?因為想讀者們自己嘗試去翻譯,最重要的原因,是因為譯者懶。。。)
Does this whole publish-subscribe thing resemble something you might already know? After giving it some thought, it’s the pretty same mechanism that you use in element.addEventListener(action, callback). You subscribe your function to a particular event, which ich being called when some action is published by element. Same story.
Going back to the title: why is this thing so bloody important? After all, in the long run, there is little sense in holding up to vanilla JavaScript and modifying the DOM manually?—?same goes with manual mechanisms for passing and receiving events. Various frameworks have their established solutions: Angular uses RxJS, React have state and props management with possibility of boosting it with redux, literally every usable framework or library have its own method of data synchronization.
Well, the truth is that all of them use some variation of publish-subscribe pattern.
As we already said?—?DOM event listeners are nothing more than subscribing to publishing UI actions. Going further: what is a Promise? From certain point of view, it’s just a mechanism that allows us to subscribe for completion of a certain deferred action, then publishes some data when ready.
React state and props change? Components’ updating mechanisms are subscribed to the changes. Websocket’s on()? Fetch API? They allow to subscribe to certain network action. Redux? It allows to subscribe to changes in the store. And RxJS? It’s a shameless one big subscribe pattern.
It’s the same principle. There are no magic unicorns under the hood. It’s just like the ending of the Scooby-Doo episode.
It’s not a great discovery. But it’s important to know:
No matter what method of solving asynchronous problem will you use, it will be always some variation of the same principle: something subscribes, something publishes.
That’s why it is so essential. You can always think of publish and subscribe. Take note and keep going. Keep building larger and more complex application with many asynchronous mechanisms?—?and no matter how difficult it may look like, try to synchronize everything with publishers and subscribers.
Still, there is a number of topics untouched in this story:
Mechanisms of unsubscribing listeners when not needed anymore,
Multi-topic subscribing (just like addEventListener allows you to subscribe to different events),
Expanded ideas: event buses, etc.
To expand your knowledge, you can review a number of JavaScript libraries that implement publish-subscribe in its bare form:
https://github.com/mroderick/...
https://github.com/Sahadar/pu...
https://github.com/shystruk/p...
Go ahead and try to use them, break them and run the debugger in order to see what happens under the hood. Also, there is a number of great articles that describe this idea very well.
You can find the code from this story in the following GitHub repository:
https://github.com/hzub/pubsu...
Keep experimenting and tinkering—and don’t be afraid of the buzz words, they’re usually just regular code in disguise. And keep thinking.
See you!
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/98865.html
摘要:主題來自于的典型面試問題列表。有多種方法來處理事件委托。這種方法的缺點是父容器的偵聽器可能需要檢查事件來選擇正確的操作,而元素本身不會是一個監聽器。 showImg(http://fw008950-flywheel.netdna-ssl.com/wp-content/uploads/2014/11/Get-Hired-Fast-How-to-Job-Search-Classifieds...
摘要:忍者級別的函數操作對于什么是匿名函數,這里就不做過多介紹了。我們需要知道的是,對于而言,匿名函數是一個很重要且具有邏輯性的特性。通常,匿名函數的使用情況是創建一個供以后使用的函數。 JS 中的遞歸 遞歸, 遞歸基礎, 斐波那契數列, 使用遞歸方式深拷貝, 自定義事件添加 這一次,徹底弄懂 JavaScript 執行機制 本文的目的就是要保證你徹底弄懂javascript的執行機制,如果...
摘要:結合與的發布訂閱模式實踐本文初衷最近恰好在公司做了一個聊天室系統,所以在系統中做了一下對進行的化改造,所以想寫篇文章總結一下,如果大家有什么更好的方法或者心得感悟,歡迎交流技術棧考慮到對并沒什么本質影響,所以本文就不涉及了業務場景基于的聊天 結合promise與websocket的發布/訂閱模式實踐 本文初衷 最近恰好在公司做了一個聊天室系統,所以在系統中做了一下對websocket進...
摘要:擁抱異步編程縱觀發展史也可以說成開發的發展史,你會發現異步徹底改變了這場游戲。可以這么說,異步編程已成為開發的根基。這也是你應盡早在上投入大量時間的一處核心知識點,這其中包含和等重要概念。這也是最突出的一項貢獻。 原文地址:Medium - Learning How to Learn JavaScript. 5 recommendations on how you should spend ...
摘要:是目前唯一一個支持同步調用的跨平臺年度上最多的個項目前端掘金年接近尾聲,在最近的幾篇文章中,會整理總結一些年度開源項目。 JS 全棧教程 - 前端 - 掘金本課程是基于阮一峰的 js 全棧教程的視頻版本,免費供大家觀看... 2016 年 10 個最佳的 CodePen 作品 - 前端 - 掘金說到 CodePen,前端開發者們肯定不會陌生。如果說 Dribbble 是設計師們聚集的圣...
閱讀 2428·2021-11-23 09:51
閱讀 2457·2021-11-11 17:21
閱讀 3097·2021-09-04 16:45
閱讀 2380·2021-08-09 13:42
閱讀 2218·2019-08-29 18:39
閱讀 2879·2019-08-29 14:12
閱讀 1279·2019-08-29 13:49
閱讀 3362·2019-08-29 11:17