js中的異步 使用像 JavaScript 這樣的語言編程時,很重要但常常被誤解的一點是,如何表達和控制持續一段時間的程序行為。 如從數據庫或文件系統中請求數據然后顯示數據、通過網絡發送數據並等待響應,或者是在以固定時間間隔執行重復任務(比如動畫)等。這些程序並不會以及運行結束,通常一部分運行在當下,另一部分運行在將來。 事實上,程序中現在運行的部分和將來運行的部分之間的關系就是異步編程的核心。 ---------------------<你不知道的Javascript> 回調函數 首先要鬧明白兩個問題: 問題一:什么是回調函數? 答:函數A中傳入函數B作為參數時,函數B即為A函數執行的回調函數。(設計JavaScript中傳值調用,函數可作為值傳遞等知識點) 問題二:回調函數都是異步嗎? 由回調函數定義可推導,並非全是異步。 //非異步 const arr = [1,2,3] const cb =item => { console.log(item) } //callback arr.map(cb) //異步 setTimeout(()=>{ console.log('setTimeout')},0) 一段代碼開始: //分析如下代碼輸出 console.log('start') setTimeout( function(){ console.log('setTimeOut') }, 1000 ); console.log('end') // start -> end -> setTimeout 以上代碼可抽象出兩個問題: 1、代碼的執行順序非線性(定義順序與執行順序不一致) 2、回調函數執行強依賴於宿主環境(setTimeout函數不是javascript自帶方法,類似有fs.readFile),導致信任問題 對於第一個問題,隨着代碼量增加,理解代碼本身的難度會劇增。如 listen( "click", function handler(evt){ setTimeout( function request(){ ajax( "http://some.url.1", function response(text){ if (text == "hello") { handler(); } else if (text == "world") { request(); } }); }, 500) ; });
當我們回顧項目,看到這一段代碼時,我們的眼球會在各個事件及其對應的回調之間周轉,未必能立即理解該代碼所表示的含義。 我們大腦中的邏輯為: A、當前,添加click事件監聽函數 B、未來(觸發click事件), 調用setTimeout C、未來的未來(500ms定時觸發),調用request D、最遠的未來(ajax請求返回),判斷返回值 這時候感慨一聲:如果代碼能像A、B、C、D一樣簡潔那該多好! 對於第二個問題,做詳細詮釋: 1、怎么理解這里說的信任問題? const cb = ()=>{console.log('可信任?')} setTimeout(cb,1000) cb只執行一次嗎? 可能這里使用了標准化的setTimeout方法,你會說yes,當然只執行一次。那么,如果你使用的是第三方庫呢? 也許可以這樣解決 let trust = false const cb = ()=>{ if(!trust){trust = true} console.log('可信任?') } setTimeout(cb,1000) 那么, • 調用cb過早(在追蹤之前); • 調用cb過晚(或沒有調用); • 調用cb的次數太少或太多; • 沒有把所需的環境 / 參數成功傳給你的回調函數; • 吞掉可能出現的錯誤或異常; 呢? 2、如何解決信任問題? 信任問題產生的根源在於,雖然由我們自己定義回調函數,但回調函數的執行卻依賴於第三方。 解決這個問題,就必須在javascript中內置異步處理模式,promise粉末登場