Promise唯卖、async ,es6異步解決方案

本文你將看到:

promise介紹以及用法
promise常用api
demo使用Promise實(shí)現(xiàn)一個(gè)簡(jiǎn)單axios
async方法

1.promise基本用法

Promise 是異步編程的一種解決方案豁延。
從語(yǔ)法上說(shuō),Promise 是一個(gè)對(duì)象,從它可以獲取異步操作的消息锌半。
我們先通過(guò)一個(gè)例子來(lái)談?wù)刾romise。

function dome(t) {
  return new Promise((resolve, reject) => {
    setTimeout(resolve, t, 'resolve');
  });
}
dome(1000).then((value) => {
  console.log(value);
})

Promise構(gòu)造函數(shù)接受一個(gè)函數(shù)作為參數(shù)寇漫,該函數(shù)的兩個(gè)參數(shù)分別是resolve和reject刊殉。
resolve函數(shù):將Promise對(duì)象的狀態(tài)從“未完成”變?yōu)椤俺晒Α辈惒讲僮鞯慕Y(jié)果,作為參數(shù)傳遞出去
reject函數(shù):將Promise對(duì)象的狀態(tài)從“未完成”變?yōu)椤笆 敝硗螅瑢惒讲僮鲌?bào)出的錯(cuò)誤冗澈,作為參數(shù)傳遞出去。
下面我們?yōu)槟阏f(shuō)說(shuō)陋葡,到底發(fā)生了什么:
當(dāng)demo執(zhí)行時(shí)亚亲,新建了一個(gè)promise實(shí)例,并且在t毫秒以后腐缤,執(zhí)行resolve函數(shù)捌归。即表示成功的函數(shù)。
then可以接受兩個(gè)回調(diào)函數(shù)作為參數(shù)岭粤。第一個(gè)代表成功時(shí)調(diào)用惜索,第二個(gè)代表失敗時(shí)調(diào)用。
因?yàn)槲覀冇|發(fā)了resolve剃浇,并且返回‘resolve’巾兆,所以,出發(fā)了then第一個(gè)回調(diào)函數(shù)會(huì)打印value會(huì)打印出 resolve虎囚。
如果角塑,promise實(shí)例即沒(méi)有執(zhí)行resolve也沒(méi)有執(zhí)行reject,則這兩個(gè)回調(diào)函數(shù)都不會(huì)調(diào)用淘讥,例如

function dome(t) {
  return new Promise((resolve, reject) => {});
}
dome(1000).then((value) => {
   console.log(1);
},(value) => {
   console.log(2)
})

運(yùn)行結(jié)果圃伶,將什么也不打印。


Paste_Image.png

如果調(diào)用resolve:

function dome(t) {
  return new Promise((resolve, reject) => {
     resolve('ok')
 });
}
dome(1000).then((value) => {
   console.log(value);
},(value) => {
   console.log(2)
})

結(jié)果為:

Paste_Image.png

如果調(diào)用reject:

function dome(t) {
  return new Promise((resolve, reject) => {
     reject('err')
 });
}
dome(1000).then((value) => {
   console.log(value);
},(err) => {
   console.log(err)
})

結(jié)果:

Paste_Image.png

那么promise的執(zhí)行的先后順序是如何呢蒲列?

let promise = new Promise((resolve, reject) => {
  console.log('p');
  resolve();
});
promise.then( () => {
   console.log('ok');
});
console.log('no');

結(jié)果:

Paste_Image.png

2.then方法的鏈?zhǔn)綄?xiě)法

promise實(shí)例調(diào)用then方法以后窒朋,返回的是一個(gè)新的promise實(shí)例。因此可以在then方法的后面繼續(xù)調(diào)用then方法蝗岖。
例如:

function dome(t) {
  return new Promise((resolve, reject) => {
      setTimeout(resolve, t, t*2);
 });
}
dome(1000).then( value => {
      return dome(value)
}).then( value => {
      console.log('sec'+value)
},err => {
      console.log('err'+err)
})

即結(jié)果為

Paste_Image.png

catch方法

Promise.prototype.catch方法是.then(null, rejection)的別名侥猩。

function dome(t) {
  return new Promise((resolve, reject) => {
      setTimeout(reject, t, '發(fā)生錯(cuò)誤');
 });
}
dome(1000).catch( err=>  {
         console.log(err)
})

結(jié)果:

Paste_Image.png

如果Promise狀態(tài)已經(jīng)變成Resolved,再拋出錯(cuò)誤是無(wú)效的抵赢。
并且Promise 對(duì)象的錯(cuò)誤會(huì)一直向后傳遞欺劳,直到被捕獲為止洛退。也就是說(shuō),錯(cuò)誤總是會(huì)被下一個(gè)catch語(yǔ)句捕獲杰标。
例如:

dome(1000).then( value => {
      return dome(value)
}).then( value => {
      console.log('sec'+value)
}).catch( err=>{})

上面代碼中,一共有三個(gè)promise對(duì)象:一個(gè)由dome產(chǎn)生彩匕,兩個(gè)由then產(chǎn)生腔剂。它們之中任何一個(gè)拋出的錯(cuò)誤,都會(huì)被最后一個(gè)catch捕獲驼仪。
所以建議使用catch方法掸犬,盡量不要用then方法的第二個(gè)參數(shù),因?yàn)閏atch可以捕獲到前面then方法中的錯(cuò)誤绪爸。

all方法

Promise.all方法可以將多個(gè) Promise 實(shí)例湾碎,包裝成一個(gè)新的 Promise 實(shí)例。
在多個(gè)實(shí)例中奠货,加入有一個(gè)實(shí)例被rejected介褥,則新的實(shí)例就會(huì)變成rejected。
只有當(dāng)所有實(shí)例狀態(tài)都變成fulfilled递惋,則新的實(shí)例狀態(tài)才會(huì)變成fulfilled柔滔。
所有實(shí)例的返回值將組成一個(gè)數(shù)組,返回給新實(shí)例萍虽。
例如

function dome1() {
  return new Promise((resolve, reject) => {
      resolve('dome1')
 });
}
function dome2() {
  return new Promise((resolve, reject) => {
      resolve('dome2')
 });
}
function dome3() {
  return new Promise((resolve, reject) => {
      resolve('dome3')
 });
}
Promise.all([dome1(),dome2(),dome3()]).then((result) => {
    console.log(result)
}).catch(err => {
    console.log(err)
})

結(jié)果

Paste_Image.png

因?yàn)槊總€(gè)勢(shì)力都是resolve 所以返回了包含每個(gè)實(shí)例返回值組成的數(shù)組睛廊。

function dome1() {
  return new Promise((resolve, reject) => {
      resolve('dome1')
 });
}
function dome2() {
  return new Promise((resolve, reject) => {
      reject('err2')
 });
}
function dome3() {
  return new Promise((resolve, reject) => {
      resolve('dome3')
 });
}
Promise.all([dome1(),dome2(),dome3()]).then((result) => {
    console.log(result)
}).catch(err => {
    console.log(err)
})

結(jié)果:

Paste_Image.png

第二個(gè)reject所以最終返回了 err

race

race和all方法類似,只不過(guò)在在邏輯上只要所有子實(shí)例中有一個(gè)狀態(tài)發(fā)生改變杉编,就會(huì)立馬把狀態(tài)傳遞給新的實(shí)例超全。在這里就不在寫(xiě)例子了。大家自己研究邓馒。
reject方法與reject方法

Promise.resolve('cxh')
// 等價(jià)于
new Promise(resolve => resolve('cxh'))

如果參數(shù)是Promise實(shí)例嘶朱,那么Promise.resolve將不做任何修改、原封不動(dòng)地返回這個(gè)實(shí)例绒净。
如果參數(shù)是一個(gè)帶有then方法的實(shí)例见咒,Promise.resolve方法會(huì)將這個(gè)對(duì)象轉(zhuǎn)為Promise對(duì)象,然后就立即執(zhí)行該對(duì)象的then方法挂疆。
加入?yún)?shù)不是對(duì)象改览,則Promise.resolve方法返回一個(gè)新的Promise對(duì)象,狀態(tài)為Resolved缤言,Promise.resolve方法的參數(shù)宝当,會(huì)同時(shí)傳給回調(diào)函數(shù)。
例如

var p = Promise.resolve('cxh');
 p.then(function (s){
    console.log(s)
});

不帶有任何參數(shù)則胆萧,Promise.resolve方法返回一個(gè)新的Promise對(duì)象庆揩,狀態(tài)為Resolved俐东。
Promise.reject()方法也會(huì)返回一個(gè)新的 Promise 實(shí)例,該實(shí)例的狀態(tài)為rejected订晌。

done方法

因?yàn)镻romise內(nèi)部的錯(cuò)誤不會(huì)冒泡到全局虏辫,我們無(wú)法捕捉到拋出的錯(cuò)誤怎么辦?
所以提出一個(gè)done方法锈拨,在回調(diào)鏈末端砌庄,保證任何錯(cuò)誤都能拋出。

Promise.prototype.done = function (fulfilled, rejected) {
  this.then(fulfilled, rejected)
    .catch(function (reason) {
      setTimeout(() => { throw reason }, 0);
    });
};

上面的方法很簡(jiǎn)單奕枢,就是給Promise添加一個(gè)done方法娄昆,在done方法內(nèi)部,用this指向promise添加then和catch方法缝彬。

finally方法

finally方法用于指定不管Promise對(duì)象最后狀態(tài)如何萌焰,都會(huì)執(zhí)行的操作。

Promise.prototype.finally = function (callback) {
  let parmise = this.constructor;
  return this.then(
    value  => parmise.resolve(callback()).then(() => value),
    reason => parmise.resolve(callback()).then(() => { throw reason })
  );
};

demo實(shí)例

下面我們用promise寫(xiě)一個(gè)異步獲取數(shù)據(jù)方法:

  class Axios {
    constructor(){}
    post(url){
        console.log(1)
        return this.getData(url,'POST')
    }
    get(url){
        console.log(2)
        return this.getData(url,'GET')
    }
    getData(url,methods){
        var promise = new Promise(function(resolve, reject){
            var client = new XMLHttpRequest();
            client.open(methods, url);
            client.onreadystatechange = handler;
            client.responseType = "json";
            client.setRequestHeader("Accept", "application/json");
            client.send();

            function handler() {
               if (this.readyState !== 4) {
                return;
               }
               if (this.status === 200) {
                  resolve(this.response);
               } else {
                  reject(new Error(this.statusText));
                }
            };
        });
        return promise;
    }
}

var axios = new Axios;
axios.get('http://localhost:3000/getData').then((value) => {
    console.log(value);
});
axios.post('http://localhost:3000/postData').then((value) => {
    console.log(value);
});
console.log("cxh")

node.js部分,我們使用express

var express = require('express');
var router = express.Router();

router.get('/', function(req, res, next) {
  res.render('index');
});
router.get('/getData', function(req, res, next) {
  res.json({ "code": '100',"result":"get success!" });
});
router.post('/postData', function(req, res, next) {
  res.json({ "code": '100',"result":"post success!" });
});
module.exports = router;

下面是我們的運(yùn)行結(jié)果:

Paste_Image.png

async 函數(shù)

async 函數(shù)只不過(guò)是Generator 函數(shù)的語(yǔ)法糖谷浅。
async函數(shù)就是將 Generator 函數(shù)的星號(hào)(*)替換成async扒俯,將yield替換成await,僅此而已壳贪。

  async function () {
        var res1 = await axios.get('http://localhost:3000/getData');
        var res2 = await axios.get('http://localhost:3000/getData?name='+res1');
       console.log(res1);
       console.log(res2);
};

當(dāng)執(zhí)行上面這段代碼的時(shí)候陵珍,遇到await就會(huì)跳出,繼續(xù)執(zhí)行下面的代碼违施,直到axios拿到數(shù)據(jù)互纯,再繼續(xù)向下下執(zhí)行。
await命令后面是一個(gè) Promise 對(duì)象磕蒲。如果不是留潦,會(huì)被轉(zhuǎn)成一個(gè)立即resolve的 Promise 對(duì)象。
假如辣往,我們需要請(qǐng)求一個(gè)api兔院,拿到返回的數(shù)據(jù),作為參數(shù)再去請(qǐng)求另一個(gè)api站削,拿到數(shù)據(jù)后在作為參數(shù)請(qǐng)求下一個(gè)坊萝,那么用then,該怎么寫(xiě)许起?依照上面的demo十偶。

axios.get('http://localhost:3000/getData').then((value) => {
    return axios.get('http://localhost:3000/getData?name='+value.code);
}).then((res) => {
    return axios.get('http://localhost:3000/getData?name='+res.code);
}).then( (value) => {
    console.log(value)
}).catch(err => {});

node部分我們改成:

router.get('/getData', function(req, res, next) {
  var cxh = req.query.name;
  if(cxh && cxh == '100'){
        res.json({ "code": '200',"result":"get2 success!" });
  }else if(cxh && cxh == '200'){
        res.json({ "code": '300',"result":"finish!" });
  } else{
        res.json({ "code": '100',"result":"get success!" });
  }
});

運(yùn)行結(jié)果

Paste_Image.png

要用n個(gè)then方法。
如果改稱async函數(shù)呢园细?

class Axios {
    constructor(){}
    post(url){
        console.log(1)
        return this.getData(url,'POST')
    }
    get(url){
        console.log(2)
        return this.getData(url,'GET')
    }
    getData(url,methods){
        var promise = new Promise(function(resolve, reject){
            var client = new XMLHttpRequest();
            client.open(methods, url);
            client.onreadystatechange = handler;
            client.responseType = "json";
            client.setRequestHeader("Accept", "application/json");
            client.send();

            function handler() {
               if (this.readyState !== 4) {
                return;
               }
               if (this.status === 200) {
                  resolve(this.response);
               } else {
                  reject(new Error(this.statusText));
                }
            };
        });
        return promise;
    }
}

var axios = new Axios;
async function getData(){
    let res = await axios.get('http://localhost:3000/getData');
    let val = await axios.get('http://localhost:3000/getData?name=' + res.code);
    let cxh = await axios.get('http://localhost:3000/getData?name=' + val.code);
    console.log(res)
    console.log(val)
    console.log(cxh)
} 
getData();

結(jié)果:

Paste_Image.png

假如惦积,上面三個(gè)await方法中有一個(gè)出現(xiàn)了reject,那么那么整個(gè)async函數(shù)都會(huì)中斷執(zhí)行猛频。
如果代買如下:

var axios = new Axios;
async function getData(){
    let res = await axios.get('http://localhost:3000/getData');
    let val = await axios.get('http://localhost:3000/getData?name=' + res.code);
    let cxh = await axios.get('http://localhost:3000/getData?name=' + val.code);
    console.log(res)
    console.log(val)
    console.log(cxh)
    let fin =  await axios.post('http://localhost:3000/postData');
} 
getData();

假如上面三個(gè)方法中有一個(gè)出錯(cuò)狮崩,那么整個(gè)函數(shù)都會(huì)執(zhí)行蛛勉,也就是最后一個(gè)雖然和前面三個(gè)沒(méi)關(guān)系,依然執(zhí)行不了睦柴。那么怎么解決诽凌?

async function getData(){
    try{
        let res = await axios.get('http://localhost:3000/getData');
        let val = await axios.get('http://localhost:3000/getData?name=' + res.code);
        let cxh = await axios.get('http://localhost:3000/getData?name=' + val.code);
        console.log(res)
        console.log(val)
        console.log(cxh)
    }catch(err){
        
    }
    
    let fin =  await axios.post('http://localhost:3000/postData');
} 

這樣就ok了。
2017年8月13日

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末坦敌,一起剝皮案震驚了整個(gè)濱河市皿淋,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌恬试,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,311評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件疯暑,死亡現(xiàn)場(chǎng)離奇詭異训柴,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)妇拯,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門幻馁,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人越锈,你說(shuō)我怎么就攤上這事仗嗦。” “怎么了甘凭?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,671評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵稀拐,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我丹弱,道長(zhǎng)德撬,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,252評(píng)論 1 279
  • 正文 為了忘掉前任躲胳,我火速辦了婚禮蜓洪,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘坯苹。我一直安慰自己隆檀,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布粹湃。 她就那樣靜靜地躺著恐仑,像睡著了一般。 火紅的嫁衣襯著肌膚如雪再芋。 梳的紋絲不亂的頭發(fā)上菊霜,一...
    開(kāi)封第一講書(shū)人閱讀 49,031評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音济赎,去河邊找鬼鉴逞。 笑死记某,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的构捡。 我是一名探鬼主播液南,決...
    沈念sama閱讀 38,340評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼勾徽!你這毒婦竟也來(lái)了滑凉?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 36,973評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤喘帚,失蹤者是張志新(化名)和其女友劉穎畅姊,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體吹由,經(jīng)...
    沈念sama閱讀 43,466評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡若未,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評(píng)論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了倾鲫。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片粗合。...
    茶點(diǎn)故事閱讀 38,039評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖乌昔,靈堂內(nèi)的尸體忽然破棺而出隙疚,到底是詐尸還是另有隱情,我是刑警寧澤磕道,帶...
    沈念sama閱讀 33,701評(píng)論 4 323
  • 正文 年R本政府宣布供屉,位于F島的核電站,受9級(jí)特大地震影響溺蕉,放射性物質(zhì)發(fā)生泄漏贯卦。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評(píng)論 3 307
  • 文/蒙蒙 一焙贷、第九天 我趴在偏房一處隱蔽的房頂上張望撵割。 院中可真熱鬧,春花似錦辙芍、人聲如沸啡彬。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,259評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)庶灿。三九已至,卻和暖如春吃衅,著一層夾襖步出監(jiān)牢的瞬間往踢,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工徘层, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留峻呕,地道東北人利职。 一個(gè)月前我還...
    沈念sama閱讀 45,497評(píng)論 2 354
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像瘦癌,于是被迫代替她去往敵國(guó)和親猪贪。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容