總是糾結js是異步還是同步?
對於JS 異步還是同步這個問題,不需要糾結太多,在執行DOM渲染
時,確實是同步執行的,也是爲了安全起見,一步一步執行,如果上一步未完成,下一步是不會運行的。
但本質還是單線程
但是對於 網絡請求 這樣的場景,一個網絡資源啥時候返回,這個時間是不可預估的,所以不能傻傻的等着,也就是這樣,設計了異步,不管返回結果,執行後就執行下一步,上一步的執行結果什麼時候返回,就什麼時候再返回。
實現異步的最核心原理,就是將callback
作爲參數傳遞給異步執行函數,當有結果返回之後再觸發callback
執行
常見的異步操作:
- 網絡請求, 如
ajax
,http.get
- IO 操作, 如
readFile
,readdir
- 定時函數, 如
setTimeout
,setInterval
網絡請求實現發展:
1、回調 2、promise 3、Generator 4、async/await
回調
對於這種傳遞過去不執行,等出來結果之後再執行的函數,叫做callback
,即回調函數,如ajax
操作中
使用場景:
- 事件回調
Node API
setTimeOut/setInterval
中的回調函數ajax
請求
優點: 簡單;
缺點: 不能 try catch
; 產生回調地獄
使用
// 比較常見的有ajax
$.ajax('http://www.wyunfei.com/', {
success (res) {
// 這裏可以監聽res返回的數據做回調邏輯的處理
}
})
// 或者在頁面加載完畢後回調
$(function() {
// 頁面結構加載完成,做回調邏輯處理
})
promise
承諾未來會執行,有三種狀態,分別是: pedding
未完成, resolved
成功, rejected
失敗。
狀態改變,只有兩種情況:
pedding
->resolved
pedding
->rejected
。當promise
狀態發生改變,就會觸發then()
裏的響應函數處理後續步驟,.then()
返回一個新的promise實例,所以可以鏈式調用
特點:
- 狀態一經改變,不會再變化
- 異步操作可以簡化爲同步操作,避免層層調用
從表面上看,可以解決回調地獄的問題,但是實際上並沒有解決,在使用中,仍然需要有.then()的不斷調用
缺點:
- 不能取消
promise
- 不能
try catch
pedding
狀態時,不知道現在進展到哪一個階段
Promise.all([1,2,3])
可以實現多個異步並行執行,同一時刻獲取最終結果的問題,當子Promise
全部完成,該Promise
完成,返回全部值的數組;當有一個子promise
失敗,該promise
失敗,返回第一個失敗的結果。
Promise.race()
同上,但只要有一個成功,就返回成功的結果
使用:
new Promise(function(resolve, reject) {
// 一段耗時的一步操作
resolve('success');
// reject('fail');
}).then(
(res)=>{console.log(res)},
(err)=>{console.log(err)}
)
Generator
generator
和函數不同的是,generator
由function* 定義
(注意多出的*號),並且,除了return
語句,還可以用yield
返回多次;可以在執行過程中多次返回
使用 .next()
next
方法可以帶一個參數,該參數就會被當作上一個yield
表達式的返回值。每一次的next()
調用,執行一次yield
使用
function* gen() {
let a = yield 111;
console.log(a);
let b = yield 222;
console.log(b);
let c = yield 333;
console.log(c);
let d = yield 444;
console.log(d);
}
let t = gen();
//next方法可以帶一個參數,該參數就會被當作上一個yield表達式的返回值
t.next(1); //第一次調用next函數時,傳遞的參數無效
t.next(2); //a輸出2;
t.next(3); //b輸出3;
t.next(4); //c輸出4;
t.next(5); //d輸出5;
try {
// r1 = yield ajax('http://url-1', data1);
// r2 = yield ajax('http://url-2', data2);
// r3 = yield ajax('http://url-3', data3);
// success(r3);
}
catch (err) {
// handle(err);
}
async/await
其實就是實現了將 Generator
函數和自動執行器(co)
,包裝在一個函數中
基本兼容上面異步方式的 缺點
使用
const fs = require('fs');
const bluebird = require('bluebird');
const readFile = bluebird.promisify(fs.readFile);
async function read() {
await readFile(A, 'utf-8');
await readFile(B, 'utf-8');
await readFile(C, 'utf-8');
//code
}
read().then((data) => {
//code
}).catch(err => {
//code
});
關注我獲取更多前端資源和經驗分享
感謝大佬們閱讀,希望大家頭髮濃密,睡眠良好,情緒穩定,早日實現財富自由~