javascript與其餘語言的經典不一樣在於,javascript是異步的,而其餘語言是同步的。這裏,咱們介紹一下javascript中異步的幾種方式。javascript
回調函數,是早期js中普遍使用的一種回調方式,jquery中的ajax方法就是經典的回調函數模式。java
回調函數的寫法中,回調是放在函數參數裏面的,執行的過程看起來沒有同步寫法那麼明瞭。jquery
在回調次數多的狀況下,可能會形成回調地獄,就是嵌套太深。因此es後面提出的異步方案也是不斷對異步作優化。git
$.ajax({
url: 'http://binnie.com/getdata',
type: 'GET',
data: {},
success: {
// success todo
},
error: {
// error todo
}
})
複製代碼
promise,是es6中提供的一種解決方案,相比於回調函數,promise的寫法更加同步化一些~es6
promise的狀態分爲三種:github
狀態的變化只有兩個過程,而且狀態一旦發生改變,就永遠不可逆。ajax
Promise是一個構造函數,咱們可使用new來進行實例化的操做。數組
咱們來看一個簡單的🌰promise
咱們能夠看到new一個Promise對象的時候,傳入的是一個函數,該函數攜帶兩個參數:resolve&reject,分別爲成功操做&失敗操做,函數內部的操做是同步執行的,因此當執行代碼的時候,立刻就能夠打印出1異步
new返回的對象是一個Promise對象,Promise對象有幾個內置的函數,這幾個函數是在Promise的同步函數執行完以後可能觸發的函數。
每次執行完其中一個函數,是能夠接着鏈式執行的,由於每次執行完一次,都會返回一個新的Promise對象。
let promise = new Promise((resolve, reject) => {
// 這裏是同步執行的
console.log(1)
// 異步操做
return resolve('binnie')
});
promise.then((resolve) => {
// 成功回調,即執行resolve()
console.log('then')
},(reject) => {
// 失敗回調,即執行reject()
console.log('err')
}).finally(() => {
// 完成回調,不管成功/失敗都會到的回調
console.log('finally')
}).catch((err) => {
// 錯誤回調,前面任何一步拋出的錯誤
console.log(err)
});
// 1
// then
複製代碼
實例方法是new Promise返回的對象的原型方法。好比then方法 -> Promise.prototype.then。
then方法接受兩個參數,第一個爲resolve回調,第二個爲reject回調
promise.then((resolve) => {
// resolve參數爲promise resolve的時候的參數
// 當promise執行resolve時,會進入該回調
},(reject) => {
// reject參數爲promise reject的時候的參數
// 當promise拋出異常 或者 執行reject時,會進入該回調
})
複製代碼
finally回調函數不接受任何參數,只作finally處理,相似請求的complete操做。
promise.finally(() => {
// resolve or reject 以後的操做
})
複製代碼
catch方法,用於捕獲異常。
在promise中的異常能夠在兩個位置進行捕獲,then方法的第二個函數 or catch方法。通常狀況下,咱們會在catch中捕獲。
在promise中,錯誤也是會冒泡的,若是then操做沒有添加reject方法,那麼錯誤就會在後面的catch捕獲,不過,若是沒有對獲取進行捕獲,錯誤其實會被promise本身吃掉,而不會暴露在最外面。
promise.then(() => {
console.log('then')
throw new Error()
}).catch((err) => {
console.log(err)
})
複製代碼
Promise方法,直接調用便可,返回的也是promise對象。
Promise.all,接受一個數組作爲參數,參數中的對象會被轉化爲promise對象
當數組中的全部promise對象resolve時,all的promise的狀態才變爲resolve
當數組中的任一promise對象reject時,all的promise的狀態就變爲reject
Promise.all 相似咱們日常使用的 &&
Promise.all([promise,2,3]).then((resolve) => {
// 參數也是一個數組,爲all數組對應的resolve參數
// ['binnie',2,3]
console.log(resolve)
}).catch((err) => {
// 錯誤爲第一個reject時拋出的錯誤
console.log(err)
})
複製代碼
Promise.race跟all的用法相似,也是接受一個數組作爲參數
數組中最早改變狀態的,那個狀態就會同步給race的狀態
Promise.race([1,promise,3]).then((resolve) => {
// 參數爲第一個狀態變爲resolve的參數
console.log(resolve)
}).catch((err) => {
// 參數爲第一個狀態變爲reject的參數
console.log(err)
})
複製代碼
返回一個狀態爲resolved的promise對象。
Promise.resolve()可攜帶參數,參數會作爲返回對象的參數。
返回一個狀態爲rejected的promise對象。
Promise.reject()可攜帶參數,參數會作爲返回對象的參數。
關於promise,也可查看樓主以前的文章 ES6之promise基礎篇
generator能夠理解爲一個暫停執行函數。每次執行next()來往下走,直到結束,狀態中止。
generator函數的寫法,就是在函數後面加一個星號 function*
,在函數內部,可使用 yield
來進行暫停的操做。
function* foo() {
console.log(0)
yield 1
yield 2
yield 3
return 4
yield 5
}
// 調用generator函數的時候,函數並不會被執行
let f = foo()
console.log(f)
// 調用next時函數纔會開始執行,當遇到yield時暫停
// 下一次next時會從上次暫停的位置繼續執行
// 當遇到return時 or 函數執行完成時,函數結束
console.log(f.next())
console.log(f.next())
console.log(f.next())
console.log(f.next())
console.log(f.next())
複製代碼
generator相對來講可讀性會比較差,因而乎有來async函數,表示函數內部會有異步操做,async函數至關因而generator函數的語法糖。
下面咱們來看下async函數的寫法
// 定義一個async函數,代表函數內部要執行異步操做
async function fun() {
// await後面跟着異步操做
// 函數會等待await操做完成以後再繼續往下走
let obj = await request();
return obj;
}
// async函數執行完以後會返回一個promise對象,函數返回值可再then中取到
let ret = fun()
ret.then((resolve) => {
console.log(resolve)
})
複製代碼
當async函數中遇到await時,就會進行等待,而後再執行下一步的操做,若是不必串行,咱們能夠改爲並行的寫法,這樣能夠節省函數執行時間。
// 串行寫法
async function fun() {
let obj1 = await request1();
let obj2 = await request2();
return;
}
// 並行寫法
async function fun() {
let obj1 = request1();
let obj2 = request2();
let ret1 = await obj1;
let ret2 = await obj2;
return;
}
// Promise.all寫法
async function fun() {
let [obj1, obj2] = await Promise.all([request1(), request2()]) ;
return;
}
複製代碼
異步操做的核心都是同樣的,只是寫法不一樣而已。
具體要採用哪一種異步方法,仍是要看咱們實際的項目須要。