一、ES6 Promise對象
const result = new Promise((resolve, reject) => { if (success) { resolve('成功'); } else { reject('失敗'); } }); result.then((res) => { console.log(res); //輸出成功 }); result.catch((res) => { console.log(res); //輸出失敗 });
常用API:
1.resolve 返回異步操作成功的結果
2.reject 返回異步操作失敗的結果
3.then 執行Promise狀態是成功的操作
4.catch 執行Promise狀態是失敗的操作
5.finally 不管Promise狀態是成功或失敗都執行的操作
Promise.all方法簡介:
Promse.all在處理多個異步處理時非常有用,比如說一個頁面上需要等兩個或多個ajax的數據回來以后才正常顯示
const p = Promise.all([p1, p2, p3])
//順序執行p1,p2,p3,即使p2的執行時間較短仍要等待p1執行完成
//p1,p2,p3為promise對象
p.then((result) => { console.log(result) //result 是個數組,按promise的順序依次返回結果 }).catch((error) => { console.log(error) })
p的狀態由p1、p2、p3決定,分成兩種情況。
(1)只有p1、p2、p3的狀態都變成fulfilled,p的狀態才會變成fulfilled,此時p1、p2、p3的返回值組成一個數組,傳遞給p的回調函數。
(2)只要p1、p2、p3之中有一個被rejected,p的狀態就變成rejected,此時第一個被reject的實例的返回值,會傳遞給p的回調函數。
二、ES6 Generator 函數
Generator函數是ES6引入的新型函數,用於異步編程,跟Promise對象聯合使用的話會極大降低異步編程的編寫難度和閱讀難度。
Generator函數跟普通函數的寫法有非常大的區別:
一是,function關鍵字與函數名之間有一個星號;
二是,函數體內部使用yield語句,定義不同的內部狀態(yield在英語里的意思就是“產出”)。
function* g() { yield 'a'; yield 'b'; yield 'c'; return 'ending'; } g(); // 返回一個對象
g().next(); // 返回Object {value: "a", done: false}
g().next(); // 返回Object {value: "b", done: false}
g().next(); // 返回Object {value: "c", done: false}
g().next(); // 返回Object {value: "ending", done: false}
g().next(); // 返回Object {value: "undefined", done: false}
generator函數中next()參數:
next方法參數的作用,是為上一個yield語句賦值。
由於yield永遠返回undefined(執行yield語句后就阻止了 例如:let y = yield '55',y返回undefined,下一個next方法中仍然是undefined)
這時候,如果有了next方法的參數,yield就被賦了值,當下一個next方法傳入值后,y就被賦予了新值。
帶參數跟不帶參數的區別是,帶參數的情況,首先第一步就是將上一個yield語句重置為參數值,然后再照常執行剩下的語句。總之,區別就是先有一步先重置值,接下來其他全都一樣。
此帶參數的方法可以用在多接口並發的處理上
generator函數在vue中的應用:
<button @click="()=>this.testM.next()">generator</button>
mounted(){
this.testM=this.go() //每次調用this.go().next()都會把原先的銷毀,所以需要先保存起來,會導致每次點擊都只會觸發第一個yield
this.testM=this.go() //每次調用this.go().next()都會把原先的銷毀,所以需要先保存起來,會導致每次點擊都只會觸發第一個yield
}
*go() {
while (true) { //實現一直循環,否則當點擊二次后就無法繼續執行了
console.log('Tick!');
yield;
console.log('Tock!');
yield;
}
},
//此示例 等同於下面的寫法
var ticking = true; var go= function() { if (ticking) console.log('Tick!'); else console.log('Tock!'); ticking = !ticking; }
Generator 函數實現的狀態機不用設初始變量,不用切換狀態,上面的Generator函數實現與ES5實現對比,
相比的優點:
可以看到少了用來保存狀態的外部變量ticking,這樣就更簡潔,更安全(狀態不會被非法篡改)、更符合函數式編程的思想,在寫法上也更優雅。
Generator+Promise實現完美異步
//接口異步發生,互不阻塞。時間消耗為2s。
var resolveAfter1Second = function() { return new Promise(resolve => { setTimeout(function() { resolve("fast"); console.log("fast promise is done"); }, 1000); }); }; var resolveAfter2Seconds = function() { return new Promise(resolve => { setTimeout(function() { resolve("slow"); console.log("slow promise is done"); }, 2000); }); }; function *dealData() { let p1 = resolveAfter1Second(); // 請求1獲取到 r1 let p2 = resolveAfter2Seconds(); // 請求2獲取到 r2
//此時2個延時操作已經異步執行了,會先后打出console的內容,但是操作結果resolve並沒有處理(並發執行操作,減少耗時)
let r1 = yield p1; //resolve的結果在這處理
let r2 = yield p2; //resolve的結果在這處理 } var gen=dealData(); //此時"fast promise is done" 和 "fast 會在1秒后輸出 var a=gen.next().value; //此時已執行一次generator函數,‘fast promise is done’已經被打出 a.then(function(res){ console.log(res) //打出resolve的結果'fast' ,由於操作早已經執行,這里的結果會和‘fast promise’同時打出,不存在延時 var b=gen.next().value.then(function(res){ //此時slot 和 slow promise is done 會在fast延遲后1秒輸出 console.log(res) }) })
//如果想要2個接口同步順序發生,則:(耗時3秒)
function *dealData() {
yield resolveAfter1Second();
yield resolveAfter2Seconds();
}
三、async
以上yield + Promise的寫法需要我們對拿到的promise的決議進行人工處理(區分成功或失敗) // .then()操作
在ES7中提供了async/await幫我們省掉了這個步驟:async/await組合的出現使得異步的世界更加完美啦~~
//使用async對上面的genrator+promise方法的進行優化,總耗時為2s
var resolveAfter2Seconds = function() { return new Promise(resolve => { setTimeout(function() { resolve("slow"); console.log("slow promise is done"); }, 2000); }); }; var resolveAfter1Second = function() { return new Promise(resolve => { setTimeout(function() { resolve("fast"); console.log("fast promise is done"); }, 1000); }); }; var concurrentStart = async function() { const slow =resolveAfter2Seconds(); // starts timer immediately const fast = resolveAfter1Second(); // starts timer immediately
//此時2個延時操作已經異步執行了,會先后打出console的內容,但是操作結果resolve並沒有處理(並發執行操作,減少耗時)
console.log(await fast); // fast的延時操作已經執行完畢,操作結果會立刻輸出 console.log(await slow); // slow的延時操作會慢1s } concurrentStart()
//如果想要同步順序執行,則:
var concurrentStart = async function() {
const slow =await resolveAfter2Seconds(); // await會阻塞下一個接口的執行
const fast =await resolveAfter1Second(); // 等待上一個awit執行完畢
console.log(fast); // fast的延時操作已經執行完畢,操作結果會立刻輸出
console.log(slow); // slow的延時操作會慢1s
}
那么async/await的寫法和yield相比孰優孰劣呢?
其實兩者都有自己獨到的長處
- async/await在處理promise的層面上省略了對決議的人工處理,讓代碼量得以減少,語義上也更容易理解。 //async 在異步接口處理上更加簡便
- yield包容性更廣泛,async只能接口promise,yield除此之外還能接收字符串、數組、對象等各種類型的數據。 //yield可以當成一個狀態機使用