1. 程式人生 > >ES6/7/8新特性Promise,async,await,fetch帶我們逃離非同步回撥的深淵

ES6/7/8新特性Promise,async,await,fetch帶我們逃離非同步回撥的深淵

Promise: 
在ES6以前如果我們需要在js中進行非同步處理,大多數都是通過使用回撥函式的方式來解決問題,如果簡單的非同步處理,回撥函式的方式看起來還是比較優雅的,逼格還有點高,但是如果非同步操作很多,回撥巢狀就很深,程式碼看起來就會特別彆扭,維護起來成本也會變高這個時候ES6帶來Promise這個新特性,這個方法很好的解決了深層次非同步巢狀的問題,我們在寫程式碼的時候可以採用類似linux流式的書寫方式: 
例如:

 flow().then(function(success){}).catch(function(error){})
  • 1

這樣的非同步操作看起來就非常直觀了。下面是一個完整Proimise例子

function flow(flag){
       //promise是一個建構函式,內部接收一個回撥函式。
       //回撥函式內部有兩個引數,reslove是執行成功的時候呼叫的,reject是失敗呼叫的
       var promise = new Promise(function(reslove,reject){
          setTimeout(function(){//模擬非同步操作
               if(flag){
                 reslove(true);//處理成功
               }else{
                 reject(false
)處理失敗 } },5000) }); return promise; } //flow返回的是一個Promise物件,這樣我們就能通過下面的方式來的處理非同步操作了。 //呼叫該函式 flow().then(function(success){ }).catch(function(error){ //處理失敗返回 });
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

reslove代表處理成功,對應後面的then,reject代表處理失敗,對應後面的catch,resolve和reject裡面值可以是字串,也可以是物件,這個值可以理解成Promise的返回值。 
Promise不經有then,catch,還有all,race,具體怎麼用找度娘。其實就是同時獲取多個Promise物件返回值得操作。

看見上面的Promise的用法,熟悉jquery的司機是不是有點似曾相識的趕腳。其實在jquery1.5版本以後,$.ajax就可以使用上面那種流式的操作了,開始逐漸擯棄回撥的方式。

$.ajax({}).then(function(result){}).fail(function(error){})
  • 1

如果你還在使用success,error的回撥函式的這種方式,呵呵。。。。 
jquey使用了Deferred的方式實現的類似Promise的功能,在jquery1.7以後,.Deferred.Deferred被獨立了出來,.Deferred和Promise的使用方式幾乎就是一個模子裡面刻出來的:

function runAsync(){
    var def = $.Deferred();
    //做一些非同步操作
    setTimeout(function(){//模擬非同步操作
        def.resolve('隨便什麼資料');
    }, 2000);
    return def;
}
runAsync().then(function(data){
    console.log(data)
});
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

講完了Proimise,現在來講講async和await 
async,await語法糖 
async和await是ES7新增的規範,兩個規範可以看成一隊連體嬰,他們要一起出現才有效果:

var stop= function (time) {
    return new Promise(function (resolve, reject) {
        setTimeout(function () {
            resolve();
        }, time);
    })
};

var start = async function () {
    // 在這裡使用起來就像同步程式碼那樣直觀
    console.log('start');
    var result = await stop(3000);
    console.log('end');
};
start();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

要注意await只能在async中使用,不然是沒有效果的。其實async 和await聯合起來使用相當於替代了Promise的then和catch方法,將async低昂一的函式裡面的程式碼由非同步變成了同步阻塞式,只有當await定義的行數執行完了程式碼才會繼續往下執行,同時await還有有返回值,他的返回值在上面這個例子中就是resolve,reject的值需要通過try {}catch(err){},捕獲:

var start = async function () {
    // 在這裡使用起來就像同步程式碼那樣直觀
    console.log('start');
    try{
      await stop(3000);
    }catch(err){
      console.log(err);
    }
    console.log('end');
};
start();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

await後面的應該跟一個Promise的物件或者fetch(後面講),不然結果會有點怪怪的,我直接在後面加一個setTimeout,結果然我很是不解,有興趣的可以分別執行下面兩段程式碼,結果超乎你的想象:

var start1 = async function () {
    // 在這裡使用起來就像同步程式碼那樣直觀
    console.log('start');
   var a = await setTimeout(function(){console.log("---")},5000)
    console.log('end'+a);
};  start1();console.log("----");
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
var start2 = async function () {
    // 在這裡使用起來就像同步程式碼那樣直觀
    console.log('start');
   var a = await setTimeout(function(){console.log("---")},5000)
    console.log('end'+a);
};  start();console.log("----");for(var i=0; i<10;i++){console.log(i)}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

其實個人感覺還是喜歡直接使用Promise實現非同步操作,感覺async和await有點把事情搞複雜了。當然居然人家把他作為了ES7的規範,而且是為數不多的規範,那麼他就有用武之地。特別是如果我們有時候js程式碼需要同步操作的時候。

fetch: 
這個方法是ES2017中新增的特性,這個特性出來後給人一種傳統ajax已死的感覺,其實它的作用是替代瀏覽器原生的XMLHttpRequest非同步請求,我們在日常的開發中,基本不會自己去寫XMLHttpRequest,主要是太複雜了,都是使用已經封裝好了的各種插,件常用的有jquery,npm包管理工具也提供了axios,request等模組。而有了fetch後我們就可以在不用這些外掛的情況下快速簡單的實現非同步請求了: 
get請求:

 var word = '123',
 url = 'https://sp0.baidu.com/5a1Fazu8AA54nxGko9WTAnF6hhy/su?wd='+word+'&json=1&p=3';
fetch(url,{mode: "no-cors"}).then(function(response) {
 return response;
}).then(function(data) {
 console.log(data);
}).catch(function(e) {
 console.log("Oops, error");
});
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

post請求:

var headers = new Headers();
headers.append("Content-Type", "application/json;");
fetch(url, {
 method: 'post',
 headers: headers,
 body: JSON.stringify({
 date: '2016-10-08',
 time: '15:16:00'
 })
});
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

上面這種寫法其實和我們常用的ajax外掛寫法就比較像了,但是要注意目前原生的fetch還不支援jsonp的請求方式,如果需要實現jsonp,需要安裝npm包fetchJ-jsonp,使用方式:

fetchJsonp(url, {
 timeout: 3000,
 jsonpCallback: 'callback'
}).then(function(response) {
 console.log(response.json());
}).catch(function(e) {
 console.log(e)
});
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

我們可以通過new Header構建我們的請求頭:

var headers = new Headers();
headers.append("Content-Type", "text/html");
fetch(url,{
 headers: headers
});
  • 1
  • 2
  • 3
  • 4
  • 5

請求頭也是可以被索引:

var header = new Headers({
 "Content-Type": "text/plain"
});
console.log(header.has("Content-Type")); //true
console.log(header.has("Content-Length")); //false
  • 1
  • 2
  • 3
  • 4
  • 5

fetch可以設定不同的模式使得請求有效. 模式可在fetch方法的第二個引數物件中定義. 
通過上面fetch的使用方式,可以看出他和Promise的使用方式非常相像,fetch其實返回的就是一個Promise物件,async,await也就和fetch是完美相容了,我們可以使用async,await實現程式碼的同步:

(async ()=>{
 try {
 let res = await fetch(url, {mode: 'no-cors'});//等待fetch被resolve()後才能繼續執行
 console.log(res);
 } catch(e) {
   console.log(e);
 }
})();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

這篇文章主要梳理了一下Promise,async,await,fetch之間的關係,Primise是基礎,async,await,fetch是踩在Promise的肩膀上。