文章加密

;

2019年12月20日 星期五

mocha... BDD,TDD... scrum,cucumber

https://mochajs.org/
很常改的程式才適合做自動化測試
有寫測試並不等於不用QA
測試程式涵蓋到80%已經算很高了

http://www.ruanyifeng.com/blog/2015/12/a-mocha-tutorial-of-examples.html
测试框架 Mocha 实例教程


https://www.jianshu.com/p/9c78548caffa  chinese ver


相較jest開箱即用的特性(針對react,最初就是設計給facebook使用,其他語言當然也可以用,但是需再作配置config且相關資料沒有mocha多),而mocha有robust documents

Chai is a BDD / TDD assertion library for node and the browser that can be delightfully paired with any javascript testing framework.

continous integration(CI)持續整合,是一種軟體工程流程,是將所有軟體工程師對於軟體的工作副本持續整合到共享主線的一種舉措。

Travis CI 是一個已經建置好而且免費的持續整合系統,並且支援多種語言。只支持 Github,不支持其他代码托管服务。
detail: http://www.ruanyifeng.com/blog/2017/12/travis_ci_tutorial.html

cucumberjs 

https://cucumber.io/docs/guides/overview/
  (ING..https://cucumber.io/docs/bdd/  Read the topics below to dig deeper and learn more about BDD.)

https://www.youtube.com/watch?v=yiMGanbHdko
what is e2e testing??以使用者端角度對程式系統端做測試

gherkins:一種語法,像是When, Then, ...
Gherkin Syntax:

Feature: Title of the Scenario
Given [Preconditions or Initial Context]
When [Event or Trigger]
Then [Expected output]

Important Terms used in Gherkin
Feature
Background
Scenario
Given
When
Then
And
But
Scenario Outline Examples

Protractorhttp://www.protractortest.org/#/tutorial
it is a node.js program built on top of WebDriverJS, there are browser, element, by that we can use.
it is an end to end(e2e) test framework for Angular and AngularJS applications(but it can also be used to test non-angular applications).
https://www.youtube.com/watch?v=p8ENoeZENhk Protractor Beginner Tutorial 1 |
Tool: protractor recorder. (Tired of Writing E2E Test? Try Recorders)
https://www.youtube.com/watch?v=emISX35OnKo

WebDriverJS  (Drivier 可能指驅動器, chrome driver, safari driver)
it is the Official javascript implementation of selenium.
it helps to interact with elements on the web.
Protractor extends WebDriverJS functionalities to enable automation of user actions on browser applications.



Selenium(未看)這先算拉,先學mocha
https://learngeb-ebook.readbook.tw/intro/selenium.html
https://selenium.dev/ 

tool website用來攥寫BBD邏輯: https://relishapp.com/kirkchen/cucumber-js-sample/docs/calculator

scrum (vs Kanban 經常會有臨時插件的工作比起用scrum更適合Kanban只要公司的安排好,並沒有說用哪個才真的好,但公司本身混亂,那用哪個都不好
daily standup:每位 Scrum team 更新自己昨天完成的事項、今天預計完成的事項以及是否有遇到什麼 block 阻礙
Sprint Review :會議由 Scrum Master 簡介這次 Sprint 所完成的 Shippable Product Increment可交付的產品增量)和 Dev Team 展示開發成果
sprint retrospective Sprint :結束時的閉門會議
Planning poker :這個過程可以讓大家對於 Story 的認知和知識可以 align 到同一條線上(例如估計點數(Story Point)不一致時彼此說明和討論,增進對於 Story 和系統的了解)
User Stroy Mapping / workshop:一般不能算是標準的 Sprint Event,通常是 Product Owner 在對於開發新產品的掌握度比較低,需要開發成員一起協助定義 User Story 所召開的會議
A burn down chart is a graphical representation of work left to do versus time
Artifact 產出物
User Story 是用使用者需求角度撰寫的有商業價值的一段敘述,而 Acceptance criteria 則是更為細部的補充描述。當有數個 story 隸屬於某一個相關 feature 時通常會把它歸為同一個 Theme,若是一個大的產品或是系統需要橫跨數個 sprint 時會把這些 Story 都歸類為同一個 Epic
detail: https://blog.kdchang.cc/2019/01/04/what-is-the-experience-of-be-a-scrum-master/  scrum的解析
detail: https://progressbar.tw/posts/66  Kanban(看板)的說明

Modeling Techniques, Business Process Modeling(BPM), Data modeling

Business Process Modeling Notation(BPMN) is a standard with well-defined syntax. So many business analysts are familiar with it which makes collaboration much easier. 
BPMN consists of the following basic building blocks;
  • Flow objects: events (circles), activities (rectangles with rounded corners), and gateways (diamonds)
  • Connecting objects: mainly comprising arrows, these indicate sequence flow (filled arrows), message flow (dashed arrows), and associations
  • Swim lanes: pools (graphic container) and lanes (sub-partition of the pool)
  • Artifacts: data objects, groups, and annotations

more BPMN example: https://creately.com/diagram-community/examples/t/bpm

User Story(使用者敘述)是一段簡單的功能敘述,以客戶或使用者的觀點撰寫下有價值的功能(functionality/feature)。與其說它是規格文件(documentation),不如說它代表(represent)客戶的一個需求而已,因為實做細節將延後至開發時才會確定。

幾個 User Stories 的範例如下:

使用者可以在網站上張貼履歷
使用者可以搜尋有哪些工作
公司可以張貼新工作
使用者可以限制誰可以看到他的履歷
初學的範本可以是:作為一個 (某個角色) 使用者,我可以做 (某個功能) 事情,如此可以有 (某個商業價值) 的好處。

As a (role of user), I want (some feature) so that (some business value).

建議能在每次開發週期(Iteration)開始的會議中(最慢也要在Iteration中期前),把會有哪些驗收測試都提供給程式設計師,並且提供必要的測試資訊。

Stories 非常適合搭配 Iteration 的開發方式,在一個開發週期內(例如兩週)內做好分析/設計/實做/測試,然後得到 feedback。我們會將 User stories 分配到各個 Iteration 中,而一個 Iteration 可以放多少 User stories 呢? 我們會估計團隊 Velocity 開發速度(一個 Iteration 可以完成多少 story points),然後根據 story 的複雜度跟大小估計 story points,再由客戶依照優先順序排入 Iteration 中。

User stories 的用意就不在於寫下完整的細節,而是一個文字記錄說提醒開發者跟客戶,這裡需要再討論溝通細節。
經驗談:
採用敏捷方法的軟體開發合約該怎麼簽? 一文所提,很多時候只能玩成價格固定、交期不變、規模求個大概。而這個規模用 User Stories 來描述,來做為專案估計時程和報價的重要參考。不過這種情況下,嗯,就沒有上述計時合約可以慢慢寫 user stories 的好處,必須一開始就把 user stories 寫比較完整。

這裡顯然就是一個拉鋸戰啊,如果一開始合約中的 user stories 可以越大概,就可以越偏向 Agile 想要的彈性。但是實際的情況,不把規模定詳細一點又不行。我認為這種所謂的實際情況,並不是單方面客戶的問題。而是號稱敏捷的接案方常常也提不出信心跟有利證明說,這N個月到底可以給出什麼東西跟保證? 如果沒有實際認真在做 Iteration、並測量實際的開發速度,接案方又怎麼能夠讓客戶相信你的 “每個月提供三個人力” 到底是不是真的有三個人在做??? 是不是在呼弄客戶?? 既然沒個辦法可以相信,那還是回到規模固定、價格固定,交期就大家心照不宣(反正十之八九會 delay),這樣還比較保險心安。detail: https://ihower.tw/blog/archives/2090
tool guide for 配合user story的機制管理專案: https://www.youtube.com/watch?v=MNLBsr657Co(未看)
User stories are more  efficient  than  use  cases  in  agile  processes(敏捷開發)  such  as Scrum and Use Cases models take more time to write.
detail: https://www.youtube.com/watch?v=MNLBsr657Co 大概是scrum的簡易介紹
detail: https://www.researchgate.net/publication/312293252_User_Stories_vs_UML_Use_Cases_in_Modular_Transformation(paper 未完)



UML(Unified Modeling Language)
There are 14 UML diagram types to help you model behaviors, like software solutions, application structures, system behavior and business processes.


UML includes Use Cases

  • visualize the functional requirements of a system that will translate into design choices and development priorities.
  • help identify any internal or external factors that may influence the system.
  • provide a good high level analysis from outside the system




  • rectangle(gray here) which contains use cases.
  • ovals are "use cases", which represent the functions within the system.
  • actors are the users of a system, which specify a role played by someone or some other system that interacts with the subject

detail: https://www.youtube.com/watch?v=Omp4RbHbB0s
tool webste: https://app.creately.com/diagram/create


Entity Relationship Diagrams (ERD)


  • a rectangle is an entity, which is an object or concept about which you want o store information.
  • a rectangle with a double outline is a weak entity, which must be defined by a foreign key relationship with another entity.
  • a diamond shape is a action, which show how two entities share information in a database.
  • ovals are attributes, a key attribute is the unique, distinguishing characteristic of the entity.(like ID number)
  • oval with a double outline can have more than one value(like skills)
  • oval with a dotted outline is based on another attribute(like the employee's monthly salary is based on the employee's annual salary)

  • Cardinality specifies how many instances of an entity relate to one instance of another entity, which specifies the maximum number of relationships
  • Ordinality describes the relationship as either mandatory or optional, which specifies the absolute minimum number of relationships.
In some cases, they may self linked. For example, employees can supervise other employees. 

detail:https://www.youtube.com/watch?time_continue=18&v=dUJp0Yoq5eg&feature=emb_logo


https://medium.com/@yurenju/%E8%87%AA%E5%8B%95%E8%BB%9F%E9%AB%94%E6%B8%AC%E8%A9%A6-tdd-%E8%88%87-bdd-464519672ac5  自動軟體測試、TDD 與 BDD
continue .. (ING)




在上面這個例子 labor 物件有 setAge(), setMonthlySalary() 等方法,當在設計這些方法前有測試案例時,我們又可以考慮這些方法都回傳 labor instance 本身,這樣就可以在使用時用 method chaining 的方式呼叫。同時我們也會釐清會有 WorkTime 與 Labor 兩個類別,其中各別包含了一些方法 (method)。(未看完)





  1. BDD ( Behavior-driven Development, 2003得名 )  https://zh.wikipedia.org/wiki/%E8%A1%8C%E4%B8%BA%E9%A9%B1%E5%8A%A8%E5%BC%80%E5%8F%91
  2. TDD ( Test-driven development, 1990提出):倡導先寫測試程序,然後編碼實現其功能。目的是取得快速反饋並使用「illustrate the main line」方法來構建程序。

    測試驅動開發是戴兩頂帽子思考的開發方式:先戴上實現功能的帽子,在測試的輔助下,快速實現其功能;再戴上重構的帽子,在測試的保護下,通過去除冗餘的代碼,提高代碼質量。測試驅動著整個開發過程:首先,驅動代碼的設計和功能的實現;其後,驅動代碼的再設計和重構。

    正面評價

    • 可以有效的避免過度設計帶來的浪費。但是也有人強調在開發前需要有完整的設計再實施可以有效的避免重構帶來的浪費。
    • 可以讓開發者在開發中擁有更全面的視角。

    負面評價

    • 開發者可能完成滿足了測試的代碼,而忽略了對實際需求的實現。有實踐者認為用結對編程的方式可以有效的避免這個問題。
    • 會放慢開發實際代碼的速度,特別對於要求開發速度的原型開發造成不利。這裡需要考慮開發速度需要包含功能和品質兩個方面,單純的代碼速度可能不能完全代表開發速度。
    • 對於GUI,資料庫和Web應用而言。構造單元測試比較困難,如果強行構造單元測試,反而給維護帶來額外的工作量。有開發者認為這個是由於設計方法,而不是開發方法造成的困難。
    • 使得開發更為關注用例和測試案例,而不是設計本身目前,對於這個觀點有較多的爭議。
    • 測試驅動開發會導致單元測試的覆蓋度不夠,比如可能缺乏邊界測試。在實際的操作中,和非測試驅動開發一樣,當代碼完成以後還是需要補充單元測試,提高測試的覆蓋度。

Callback(回調)是什麼? 這裡面還有幾個延伸連結,像mongoDB, fs等等可以讀讀

Callback(回調)是什麼?

中文翻譯字詞會用"回呼""、"回叫"、"回調",都是聽起來很怪異的講法,Callback在英文是"call back"兩個單字的合體,你應該有聽過"Call me back"的英文,實際情況大概是有客戶打來電話給你,可是你正在電話中,客戶會留話說請你等會有空時再"回電"給它,這裡的說法是指在電信公司裡的callback的涵意。而在程式開發上上,callback它的使用情境其實也是有類似的地方。

CPS風格與直接風格

延續傳遞風格(Continuation-passing style, CPS),它的對比是"直接風格(Direct style)",這兩種是程式開發時所使用的風格,CPS早在1970年代就已經被提出來了。CPS用的是明確地移轉控制權到下一個函式中,也就是使用"延續函式"的方式,一般稱它為"回調函式"或"回調(Callback)"。回調是一個可以作為傳入參數的函式,用於在目前的函式呼叫執行最後移交控制權,而不使用函式回傳值的方式。直接風格的控制權移交是不明確的,它是用回傳值的方式,然後進行到下一行程式碼或呼叫接下來其他函式。下面以範例來說明會比較容易。
直接風格的範例如下,其實就是一般函式呼叫的方式,或是用回傳的方式:
//直接風格
function func(x) {
  return x
}
CPS風格就不是這樣,它會用另一個函式作為函式中的傳入參數的樣式來撰寫程式,然後將本來應該要回傳的值(不限定只有一個),傳給下一個延續函式,繼續下個函式的執行:
//CPS風格
function func(x, cb) {
  cb(x)
}
以明確的程式流程的例子來說,假設現在要從資料庫獲取某個會員的資料,然後把裡面的大頭照片輸出。
用直接風格的寫法:
function getAvatar(user){
   //...一些程式碼
   return user
}

function display(avatar){
  console.log(avatar)
}

const avatar = getAvatar('eddy')
display(avatar)
用CPS風格的寫法,像下面這樣:
function getAvatar(user, cb){
  //...一些程式碼
   cb(user)
}

function display(avatar){
  console.log(avatar)
}

getAvatar('eddy', display)
長久以來在程式語言開發界,直接風格的程式碼是最常被使用的,因為它容易被學習與理解,一個步驟接著一個步驟,學校的程式語言課程大部份也是用這種風格來教學,不論在個人電腦上的、在伺服器端的程式語言設計通常也是這樣。主要是因為個人電腦端或是伺服器端,通常使用多執行緒或改進底層運作的執行方式,來解決多工或並行的問題。CPS風格反而很少被使用,並沒有明顯的誘因讓程式設計師一定要用CPS風格,而且也不是所有的程式語言都能使用CPS風格,在過去CPS風格並不算是主流的程式開發寫作風格。
CPS風格相較於直接風格還有一些明顯的缺點:
  • 在愈複雜的應用情況時,程式碼愈不易撰寫與組織,維護性與閱讀性也很低
  • 在錯誤處理上較為困難
JavaScript中會大量使用CPS風格,除了它本身可以使用這種風格外,其實是有原因:
  • 只有單執行緒,在瀏覽器端只有一個使用者,但事件或網路要求(AJAX)要求不能阻塞其他程式的進行,但這也僅限在這些特殊的情況。不過在伺服器端的執行情況都很嚴峻,要能同時讓多人連線使用,必需要達到不能阻塞I/O,才能與以多執行緒執行的伺服器一樣的執行效益。
  • 一開始就是以CPS風格來設計事件異步處理的模型,用於配合異步回調函式的執行使用。
基本上一個程式語言要具有高階函式(High Order Function)的特性才能使用CPS風格,也就是可以把某個函式當作另一函式的傳入參數,也可以回傳函式。除了JavaScript語言外,具有高階函式特性的程式語言常見的有Python、Java、Ruby、Swift等等。

異步回調函式

並非所有的使用callbacks(回調)函式的API都是異步執行的,但CPS的確是一種可以確保異步回調執行流程的風格。在JavaScript中,除了DOM事件處理中的回調函式9成9都是異步執行的,語言內建API中使用的回調函式不一定是異步執行的,也有同步執行的例如Array.forEach要讓開發者自訂的callbacks(回調)的執行轉變為異步,有以下幾種方式:
  • 使用計時器(timer)函式: setTimeoutsetInterval
  • 特殊的函式: nextTicksetImmediate
  • 執行I/O: 監聽網路、資料庫查詢或讀寫外部資源(???)
  • 訂閱事件(???)
針對callbacks(回調)函式來說,異步與同步的執行到底是差在那裡?你可能會產生疑惑。下面用個簡單的例子來說明。
function aFunc(value, callback){
  callback(value)
}

function bFunc(value, callback){
  setTimeout(callback, 0, value)
}

function cb1(value){ console.log(value) }
function cb2(value){ console.log(value) }
function cb3(value){ console.log(value) }
function cb4(value){ console.log(value) }

aFunc(1, cb1)
bFunc(2, cb2)
aFunc(3, cb3)
bFunc(4, cb4)
aFunc是一個簡單的回調結構,callback回調函式被傳入後最後以value作為傳入參數執行。
bFunc函式則是包裹了一個setTimeout內建方法,它可以在一定時間內(第二個參數)執行第一個參數,也就是setTimeout會執行的回調函式,第三個參數是要加入到回調函式的傳入參數值。
aFunc中使用了一般的回調函式,只是傳入到函式中當作參數,然後最後執行而已,這種是同步執行的回調函式,只是用了CPS風格的寫法。
bFunc中使用了計時器APIsetTimeout會把傳入的回調函式進行異步執行,也就是先移到工作佇列中,等執行主執行緒的呼叫堆疊空了,在某個時間回到主執行緒再執行。所以即使它的時間設定為0秒,裡面的回調函式並不是立即執行,而是會暫緩(延時)執行的一種回調函式,一般稱為異步回調函式。
最後的執行結果是1 -> 3 -> 2 -> 4,也就是說,所有的同步回調函式都執行完成了,才會開始依順序執行異步的回調函式。如果你在瀏覽器上測試這個程式,應該會明顯感受到,2與4的輸出時,會有點延遲的現象,這並不是你的瀏覽器或電腦的問題,這是因為不論你設定的setTimeout為0,它要回到主執行緒上執行,仍然需要按照內部事件迴圈所設定的時間差,在某個時間點才會回來執行。
這個程式執行的流程,可以看這個在loupe網站的流程模擬,輸出一樣在瀏覽器的主控台中可以看到。
由這個範例中,可以看到異步回調函式執行比同步回調函式更慢,異步回調函式還有另一個名稱是延時回調(defer callback),是用延時執行特性來命名。這只是一種因應特別情況所採用的函式執行方式,例如需要與外部資源存取(I/O)、DOM事件處理或是計時器的情況。等待的時間則是在Web API中,等有外部資源有回應了(或超時)才會加到佇列中,佇列裡並不會執行函式中的程式碼,只是個準備排隊進入主執行緒的機制,函式一律在主執行緒中執行。
關於函式的異步執行與事件迴圈一些原理的說明,請再參考[異步執行與事件迴圈]的章節裡的內容。


補充:
defer: deferred(延遲的縮寫) 

在<script中加入async 或defer,兩者都是異步執行script,但defer確保DOM已經完整渲染

且在DOMContentLoaded前先去執行。



async:

  1. async操作沒有辦法確保DOM都已經全部渲染
  2. 操作DOM可能等於找死,會QQ饅頭等著吃exception
  3. 所以這種async你比較常會在google analytics上看到,而非UI類庫。
  4. 請求回來的執行是會停止browser parsing喔

reference: https://realdennis.medium.com/html-script-%E4%B8%ADdefer%E8%B7%9Fasync%E6%98%AF%E4%BB%80%E9%BA%BC-1166ee88d18

+

回調函式的複雜性

callback(回調)運用在瀏覽器端似乎並沒有想像中複雜,一個事件的處理範例大概會像下面這樣:
const el = document.getElementById('myButton')

el.addEventListener( 'click', function(){
     console.log('hello!')
}, false)
你也可以把callback寫成另一個函式定義,看起來會更清楚:
function callback(){
     console.log('hello!')
}

const el = document.getElementById('myButton')

el.addEventListener('click', callback, false)
AJAX是另一個常使用的情況,內建的XMLHttpRequest物件的行為類似於事件處理,而且都打包好好的。實際上onreadystatechange這個屬性,就是XMLHttpRequest物件在處理事件用的callback(回調)函式。以下為一個簡單的範例:
var xhr = new XMLHttpRequest();

xhr.onreadystatechange = function() {
    if (xhr.readyState == XMLHttpRequest.DONE ) {
       if (xhr.status == 200) {
           document.getElementById('myDiv').innerHTML = xhr.responseText
       }
       else if (xhr.status == 400) {
          console.log('There was an error 400')
       }
       else {
           console.log('something else other than 200 was returned')
       }
    }
}

xhr.open('GET', 'ajax_info.txt', true)
xhr.send()
AJAX就是定義了
XMLHttpRequest.onreadystatechange = callback;
讓developers可以直接塞入想要的function when the readyState changes.

"匿名函式"、"函式定義"與"函式呼叫"的混合

我會認為回調函式會複雜的原因是主要是來自"匿名函式"、"函式定義"與"函式呼叫"的混合寫法。所以當在看程式碼時,你的腦袋很容易打結。
+

function func(x, cb){
  cb(x)
}

func(123456, function(value){
  console.log(value)
})
這例子很簡單,要分作幾個部份來看:
這是一個完整的"函式呼叫",也就是說它是一個被執行的語句結構:
func(123456, function(value){
  console.log(value)
})
但其中的這一段是什麼,這個是一個"函式定義",而且還是個"匿名函式"定義,它是一個callback(回調)函式的定義,它代表了func函式執行完後要作的下一件事,這個定義是在func函式中的程式碼的最後一句被呼叫執行。:
function(value){
  console.log(value)
}
所以整個語法是代表"在函式呼叫時,要寫出下一個要執行的函式定義",這就是常見回調函式的語法樣式。當然,你可以另外用一個函式來寫得更清楚:
function func(x, cb){
  cb(x)
}

function callback(value){
  console.log(value)
}

func(123456, callback)
不過,你可以發現幾件事情:
  • callback(回調)的函式名稱,可以用匿名函式取代。(實際上callback的名稱在除錯時很有用,可以在錯誤的堆疊上指示出來)
  • callback(回調)因為是函式的定義,所以傳入參數value的名稱叫什麼其實都可以。
  • callback(回調)其實有Closure(閉包)結構的特性,可以獲取到func中的傳入參數,以及裡面的定義的值。(實際上JavaScript中只要函式建立就會有閉包產生)
那麼要說到callback(回調)的最大優點,就是它給了程式開發者很大的彈性,允許開發者可以自訂下一個要執行函式的內容,等於說它可以提高函式的擴充性與重覆使用性

回調地獄(Callback Hell)

複雜的情況是在於CPS風格使用callback(回調)來移往下一個函式執行,當你開始撰寫一個接著一個執行的流程,也就是一個特定工作的函式呼叫後要接下一個特定工作的函式時,就會看到所謂的"回調地獄"的結構,像下面這樣的例子:
step1(x, function(value1){
  //do something...
  step2(y, function(value2){
    //do something...
    step3(z, function(value3){
        //do something...
    })
  })
})
它的執行順序應該是step1 -> step2 -> step3沒錯,這三個都可能是已經寫好要作某件特定工作的函式。所以真正是這樣的流程嗎?你可能忘了匿名函式(callback)也是一個函式,所以執行的步驟是像下面這樣才對:
  1. step1執行後,"value1"已經有值,移往function(value1)執行
  2. function(value1)執行到step2step2執行到最後,"value2"已經有值,移往function(value2)執行
  3. function(value2)執行到step3step3執行到最後,"value3"已經有值,移往function(value3)執行
  4. function(value3)執行完成
寫成流程大概是像下面這樣的順序,一共有6個函式要執行的流程,其中的這三個匿名回調函式的主要工作,是負責準備接續下一個要執行特定工作的函式:
step1 -> function(value1) -> step2 -> function(value2) -> step3 -> function(value3)
那為何為不使用直接風格?而一定要用這麼不易理解的程式流程結構。上面已經有講為什麼JavaScript中會大量的使用CPS的原因:
因為有些I/O或事件類的函式,用直接風格會造成阻塞,所以要寫成異步的回調函式,也就是一定要用CPS
你可能會認為阻塞有這麼容易發生嗎?是的,在JavaScript中要"阻塞"太容易了,它是單執行緖執行的設計,一個比較長時間的程序執行就會造成阻塞,下面的for迴圈就會讓你的按鈕按下去沒反應,而且幾個訊息都要一段時間執行完才會顯示出來:
const el = document.getElementById('myButton')

el.addEventListener( 'click', function(){
     alert('hello!')
}, false)

const aArray = []
for(let i=0; i< 100000000;i++){
  aArray[i] = i+10
}

console.log('aArray done!')

const bArray = []
for(let i=0; i< 100000000;i++){
  bArray[i] = i*10
}

console.log('bArray done!')
或許你會認為在瀏覽器上讓使用者等個幾秒鐘不會怎麼樣,但如果在要求能讓多個使用者同時使用的伺服器上,每個使用者都來阻塞主執行緒幾秒,這個伺服器程式就可以廢了。不過,以異步執行的異步回調函式並不代表就不會阻塞,也有可能從佇列回到主緒行緒後,因為需要CPU密集型的運算,仍然會阻塞到緒行緒的進行。異步回調函式,只是暫時先移到佇列中放著,讓它先不干擾目前的主執行緒的執行而已。這是JavaScript為了在只有單執行緒的情況,用來達成並行(concurrency)模型的設計方式。
如果要配合JavaScript的異步處理流程,也就是非阻塞的I/O處理,只有CPS可以這樣作。
在伺服端的Node.js一開始就使用了CPS作為主要的I/O處理方式,老實說是一個不得已的選擇,當時沒有太多的選擇,而且這原本就是JavaScript中對異步回調函式的設計。Node.js使用error-first(以錯誤為主)的CPS風格,因為考慮到callback(回調)要處理錯誤不容易,所以要優先處理錯誤,它的主要原則如下:
  • callback的第一個參數保留給Error(錯誤),當錯誤發生時,它將會以第一個參數回傳。
  • callback的第2個參數保留給成功回應的資料。當沒有錯誤發生時,error(即第一個參數)會設定為null,然後將成功回應的資料傳入第二個參數。
一個典型的Node.js的回調語法範例如下:
+

var fs = require('fs');

fs.readFile('foo.txt', 'utf8', function(err, data) {
   if(err) {
    console.log('Unknown Error');
    return;
  }
  console.log(data);
});
//  fs means "file system", more details in http://nodejs.cn/api/fs.html#fs_file_system (未看)
而且似乎是node.js的
Node.js使用CPS風格在複雜的流程時,很容易出現回調地獄的問題,這是因為在伺服器端的各種I/O處理會相當頻繁而且複雜。像下面這個資料庫連接與查詢的範例,出自Node.js MongoDB Driver API:
// A simple query using the find method on the collection.

var MongoClient = require('mongodb').MongoClient,
  test = require('assert');
MongoClient.connect('mongodb://localhost:27017/test', function(err, db) {

  // Create a collection we want to drop later
  var collection = db.collection('simple_query');

  // Insert a bunch of documents for the testing
  collection.insertMany([{a:1}, {a:2}, {a:3}], {w:1}, function(err, result) {
    test.equal(null, err);

    // Peform a simple find and return all the documents
    collection.find().toArray(function(err, docs) {
      test.equal(null, err);
      test.equal(3, docs.length);

      db.close();
    });
  });
});
//  關於上面的語法 toArray() and find(), 這裡有詳細解釋https://docs.mongodb.com/manual/reference/method/cursor.toArray/
https://docs.mongodb.com/manual/reference/method/db.collection.find/#db.collection.find
然後發現mongo也是collection, document的DB集合(像 firebase)
而且似乎配合node.js 挺好的
上面這個範例還算簡單,但裡面的回調函式有3個,函式呼叫了11個,再複雜的話就會更不好維護,我會認為這是一種舊時代的程式碼組織方式的弊病,或是當時不得已的解決方案,當可以採用更好的語法結構來取代它時,這種語法未來大概只會出現在教科書中。現在這種寫法都是一般都已經不建議使用。
現在已經有很多協助處理的方式,回調地獄可以用例如Promise、generator、async/await之類的語法結構,或是Asyncco外部函式庫等,來改善或重構原本的程式碼結構,在往後維護程式碼上會比較容易,這些才是你現在應該就要學習的方式。
此外,隨著技術不斷的進步,現在的JavaScript也已經有可以讓它使用其他執行緒的技術,有Web Worker,或是專門給Node.js使用的child_process模組與cluster(叢集)模組。






continue...  這篇裡面很多說到node.js,讓我想看node啊~

reference: https://eyesofkids.gitbooks.io/javascript-start-from-es6/content/part4/callback.html