javascript處理異步的三種方法


一、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
  }
  
  *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可以當成一個狀態機使用

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM