JavaScript學習筆記(5) 異步-- Promise

寫在前面

異步編程對Javascript語言非常重要,在Javascript的發(fā)展道路上蚊惯,異步編程的方法也是一直在不斷更新。關(guān)于這方面的知識,網(wǎng)上已經(jīng)有很多成熟的教程和講解瑟由,我將對這些教程進行整理和歸納,整理出異步JS異步編程的幾種解決方法冤寿。

Javascript的異步執(zhí)行

Javascript的執(zhí)行環(huán)境是單線程的歹苦,所謂的單線程就是一次只能完成一個任務(wù),其任務(wù)的調(diào)度方式就是排隊督怜,這就和超市等待付款一樣殴瘦,前面的人還沒有結(jié)賬,后面的人就只能等著号杠。這種模式的壞處很明顯蚪腋,就是如果有一個任務(wù)耗時很長,就會拖延整個程序的執(zhí)行。為了解決這個問題屉凯,Javascript將任務(wù)的執(zhí)行模式分為兩種:同步(synchronous)和異步(Asynchronous)动遭。

  • 同步模式就是前面提到的這種,后一個任務(wù)等待前一個任務(wù)結(jié)束再執(zhí)行神得,程序執(zhí)行的順序與任務(wù)排列的順序是一致的,同步的偷仿。
  • 異步模式哩簿,通俗點說,就是前面排隊的人告訴后面排隊的一個準確時間酝静,這樣后面的人就可以利用這段時間去干些別的事情节榜。異步模式可以通過回調(diào)函數(shù)實現(xiàn),假設(shè)我們需要進行一個耗時的數(shù)據(jù)請求别智,在對外部數(shù)據(jù)發(fā)送請求后宗苍,程序的執(zhí)行權(quán)將會交給別的任務(wù),一直等到外部數(shù)據(jù)返回以后薄榛,系統(tǒng)才會通知執(zhí)行回調(diào)函數(shù)讳窟,而回調(diào)函數(shù)中通常會包括對獲取的數(shù)據(jù)的處理方法。所以敞恋,在這個模式下丽啡,程序的執(zhí)行順序和任務(wù)的排列順序是不一致的,異步的硬猫。

Promise

Promise是異步編程的一種解決方案补箍,ES6原生提供了Promise對象。

特點

  • Promise對象有三種狀態(tài):Pending(進行中)啸蜜,Resolved(已完成)坑雅,Rejected(已失敗),只有異步操作的結(jié)果可以決定當前是哪種狀態(tài)衬横。
  • Promise的狀態(tài)改變可能有兩種情況:pending->Resolved或者pending->Rejected,一旦發(fā)生裹粤,狀態(tài)就會保持不變。

基本用法

  • Promise對象是一個構(gòu)造函數(shù)冕香,用來生成Promise實例蛹尝。
  • Promise構(gòu)造函數(shù)接受一個函數(shù)作為參數(shù),該函數(shù)的兩個參數(shù)分別為resolve和reject,他們是兩個函數(shù)悉尾,由JS引擎提供突那,不用自己部署。
  • resolve函數(shù)在異步操作成功時調(diào)用构眯,并將異步操作的結(jié)果愕难,作為參數(shù)傳遞出去。
  • reject函數(shù)在異步操作失敗時調(diào)用,并將異步操作報出的錯誤猫缭,作為參數(shù)傳遞出去葱弟。
  • then方法可以接受兩個回調(diào)函數(shù)作為參數(shù),第一個回調(diào)函數(shù)時Promise對象的狀態(tài)變?yōu)閞esolved時候調(diào)用猜丹,第二個是Promise對象狀態(tài)變?yōu)閞eject時調(diào)用芝加,第二個函數(shù)可選。
  • 如果Promise對象狀態(tài)變?yōu)閞eject時射窒,會調(diào)用catch方法指定的回調(diào)函數(shù)處理這個錯誤藏杖,所以,除了定義then的第二個參數(shù)以外脉顿,也可以定義catch方法的回調(diào)函數(shù)來處理Promise拋出的錯誤蝌麸。
例1 (簡單例子)
function test(resolve,reject){
    var timeout=Math.random()*2;
    console.log('設(shè)置timeout為'+timeout+'秒');
    setTimeout(function () {
      if(timeout<1){
        console.log('call resolve()');
        resolve(" resolved");
      }else {
        console.log('call reject()');
        reject(" rejected");
      }
    }, timeout*1000);
}

//寫法1
var p1=new Promise(test);
p1.then(function(result){
  console.log("成功"+result);
},function(result){
  console.log("失敗"+result);
});

//寫法2
var p1=new Promise(test);
var p2=p1.then(function(result){
  console.log("成功"+result);
});

var p3=p2.catch(function(result){
  console.log("失敗"+result);
})

/*
輸出:
   設(shè)置timeout為1.3983493377635763秒
   call reject()
   失敗 rejected
或者:
   設(shè)置timeout為0.8257771710631485秒
   call resolve()
   成功 resolved
  */

這個例子中p1是promise實例,方法一和方法二所表達的意思是一樣的艾疟,只是寫法不同来吩,方法1用的就是上面說過的給then傳遞兩個回調(diào)函數(shù)作為參數(shù)的寫法。方法二只將then用于函數(shù)執(zhí)行成功的情況蔽莱,而使用catch來處理函數(shù)執(zhí)行不成功的情況弟疆。一般傾向于使用catch方法定義reject狀態(tài)的回調(diào)函數(shù)。
上述的test函數(shù)執(zhí)行成功的情況下碾褂,即隨機數(shù)小于1的情況下兽间,我們將調(diào)用resolve(' resolved'),在執(zhí)行失敗的情況下正塌,將調(diào)用reject(" rejected")嘀略。變量p1是一個Promise對象,負責執(zhí)行test函數(shù)乓诽。當test函數(shù)執(zhí)行成功時帜羊,then方法指定的回調(diào)函數(shù),將在當前腳本所有同步任務(wù)執(zhí)行完后執(zhí)行鸠天,也就是過了隨機數(shù)產(chǎn)生的秒數(shù)以后執(zhí)行這里的console.log("成功"+result)讼育。相同的,當test函數(shù)執(zhí)行失敗時稠集,catch方法中指定的回調(diào)函數(shù)也會在test中的所有任務(wù)執(zhí)行完后執(zhí)行奶段。
上面提到的console.log("成功"+result)中的result其實就是等于" resolved",因為如果resolve函數(shù)和reject函數(shù)帶有參數(shù)剥纷,那么它們的參數(shù)就會被傳遞給回調(diào)函數(shù)痹籍。通常情況下,reject函數(shù)的參數(shù)會使Error對象的實例晦鞋,表示拋出的錯誤蹲缠;resolve函數(shù)的參數(shù)可能是一個正常值棺克,也有可能是另一個Promise實例,表示異步操作的結(jié)果可能是另一個異步操作线定。接下來就舉一個執(zhí)行若干個異步任務(wù)的例子.

例2 (若干個異步任務(wù))
function add(input){
  return new Promise(function(resolve,reject){
    var result=input+input;
    console.log("add");
    setTimeout(function(){
      if(result<1000){
        resolve(result);
      }else {
        reject(result);
      }
    },500);
  })
}

function multiply(input){
  return new Promise(function(resolve,reject){
    var result=input*input;
    console.log("multiply");
    setTimeout(resolve,500娜谊,result);
  })
}

var p=new Promise(function(resolve,result){
  console.log("start new promise");
  resolve(10);
})

p.then(add).then(multiply).then(add).then(add).then(function(result){
  console.log("小于1000"+result);
},function(result){
  console.log("大于1000"+result);
});
//輸出:大于1000,1600

上面這段代碼首先定義了add和multiply斤讥,然后在add()函數(shù)中定義了成功和失敗條件纱皆,就是當數(shù)字小于1000的時候未成功,大于1000的時候為失敗芭商。這里最重要的點是add和multiply都會返回一個Promise對象抹剩,所以在第一個Promise對象p運行結(jié)束后,會開始調(diào)用函數(shù)add蓉坎,并將10傳遞給函數(shù)add當作input,此時multiply方法數(shù)就會等待add返回的這個新的promise對象發(fā)生變化胡嘿,依次類推蛉艾,最后的一個then方法指定的回調(diào)函數(shù)就會等待最后一個add返回的新的promise對象狀態(tài)發(fā)生變化,如果變?yōu)镻romise對象的狀態(tài)為成功衷敌,就會調(diào)用console.log("小于1000"+result);勿侯,反之調(diào)用console.log("大于1000"+result);

例3 (all和race)

all方法將用于多個Promise實例,包裝成一個新的Promise實例缴罗。

  var p=Promise.all([p1,p2,p3]);

在使用all的情況下助琐,p的狀態(tài)由p1,p2,p3決定:

  • 只有p1,p2,p3的狀態(tài)都變成Resolved,p的狀態(tài)才會變成Resolved面氓。
  • p1,p2,p3中只要有一個被Rejected兵钮,p的狀態(tài)就變成了Rejected。
  var p1=new Promise(function(resolve,reject){
    setTimeout(resolve,1000,"p1 finish in 1s");
  });

  var p2=new Promise(function(resolve,reject){
    setTimeout(resolve,2000,"p2 finish in 2s");
  });

  Promise.all([p1,p2]).then(function(result){
    console.log("end"+result);
  });
  //直到2s后'p1 finish in 1s'和‘p2 finish in 2s’會被同時輸出

上例中舌界,因為只有p1,p2都為Resolved掘譬,由它們包裝成的新的Promise實例的狀態(tài)才會變?yōu)镽esolved,所以直到2秒以后呻拌,p1和p2的返回值才會被同時傳遞給新的promise實例的回調(diào)函數(shù)葱轩。

race方法和all類似,同樣將多個Promise實例包裝成一個新的Promise實例藐握。

  var p=Promise.race([p1,p2,p3]);

在使用race的情況下靴拱,只要p1,p2,p3之中有一個率先改變狀態(tài),p的狀態(tài)就跟著改變猾普。那個率先改變的Promise實例的返回值袜炕,就傳遞給p的回調(diào)函數(shù)。

var p1=new Promise(function(resolve,reject){
  setTimeout(resolve,1000,"p1 finish in 1s");
});

var p2=new Promise(function(resolve,reject){
  setTimeout(resolve,2000,"p2 finish in 2s");
});

Promise.race([p1,p2]).then(function(result){
  console.log("end"+result);
});
//1秒后'p1 finish in 1s'和‘p2 finish in 2s’就會被同時輸出

上例中因為p1執(zhí)行的較快抬闷,它的狀態(tài)會率先變?yōu)镽esolved妇蛀,所以p1的返回值耕突,就傳遞給了新的promise實例的回調(diào)函數(shù)。

Promise應用于Ajax

使用Promise簡化Ajax異步處理:

function ajax(method,url,data){
  var request=new XMLHttpRequest();
  return new Promise(function(resolve,reject{
    request.onreadystatechange=function(){
      if(request.readyState==4){
        if(request.status==200){
          resolve(request.responseText);
        }else {
          reject(request.status);
        }
      }
    });
  request.open(method,url);
  request.send(data);

  })
}

var p=ajax('POST',someUrl);
p.then(function(text){
  alert(text);//成功獲取到數(shù)據(jù)
}).catch(function(status){
  alert(status); //請求數(shù)據(jù)失敗评架,獲得相應代碼
});

這里的method可以是'GET'或者'POST',url是php或者asp文件的地址眷茁。

總結(jié)

關(guān)于Promise的大部分知識已經(jīng)在本文涵蓋到,不得不說廖雪峰和阮一峰老師的講解很全面纵诞,但也許有些同學對于ES6并不熟悉上祈,直接看阮一峰的Promise這章會有點吃力,所以筆者盡可能地在他們的基礎(chǔ)上解釋地更加細致一些浙芙,當然登刺,想要全面地了解Promise的所有內(nèi)容,還是要靜下心來閱讀ES6入門這本書嗡呼。這是JS異步的第一篇纸俭,接下來仍會介紹別的JS異步的解決方案。

推薦閱讀

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末南窗,一起剝皮案震驚了整個濱河市揍很,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌万伤,老刑警劉巖窒悔,帶你破解...
    沈念sama閱讀 216,496評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異敌买,居然都是意外死亡简珠,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評論 3 392
  • 文/潘曉璐 我一進店門虹钮,熙熙樓的掌柜王于貴愁眉苦臉地迎上來聋庵,“玉大人,你說我怎么就攤上這事芙粱≌洳撸” “怎么了?”我有些...
    開封第一講書人閱讀 162,632評論 0 353
  • 文/不壞的土叔 我叫張陵宅倒,是天一觀的道長攘宙。 經(jīng)常有香客問我,道長拐迁,這世上最難降的妖魔是什么蹭劈? 我笑而不...
    開封第一講書人閱讀 58,180評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮线召,結(jié)果婚禮上铺韧,老公的妹妹穿的比我還像新娘。我一直安慰自己缓淹,他們只是感情好哈打,可當我...
    茶點故事閱讀 67,198評論 6 388
  • 文/花漫 我一把揭開白布塔逃。 她就那樣靜靜地躺著,像睡著了一般料仗。 火紅的嫁衣襯著肌膚如雪湾盗。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,165評論 1 299
  • 那天立轧,我揣著相機與錄音格粪,去河邊找鬼。 笑死氛改,一個胖子當著我的面吹牛帐萎,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播胜卤,決...
    沈念sama閱讀 40,052評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼疆导,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了葛躏?” 一聲冷哼從身側(cè)響起是鬼,我...
    開封第一講書人閱讀 38,910評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎紫新,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體李剖,經(jīng)...
    沈念sama閱讀 45,324評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡芒率,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,542評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了篙顺。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片偶芍。...
    茶點故事閱讀 39,711評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖德玫,靈堂內(nèi)的尸體忽然破棺而出匪蟀,到底是詐尸還是另有隱情,我是刑警寧澤宰僧,帶...
    沈念sama閱讀 35,424評論 5 343
  • 正文 年R本政府宣布材彪,位于F島的核電站,受9級特大地震影響琴儿,放射性物質(zhì)發(fā)生泄漏段化。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,017評論 3 326
  • 文/蒙蒙 一造成、第九天 我趴在偏房一處隱蔽的房頂上張望显熏。 院中可真熱鬧,春花似錦晒屎、人聲如沸喘蟆。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,668評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蕴轨。三九已至港谊,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間尺棋,已是汗流浹背封锉。 一陣腳步聲響...
    開封第一講書人閱讀 32,823評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留膘螟,地道東北人成福。 一個月前我還...
    沈念sama閱讀 47,722評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像荆残,于是被迫代替她去往敵國和親奴艾。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,611評論 2 353

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

  • Promiese 簡單說就是一個容器内斯,里面保存著某個未來才會結(jié)束的事件(通常是一個異步操作)的結(jié)果蕴潦,語法上說,Pr...
    雨飛飛雨閱讀 3,358評論 0 19
  • Promise的含義: ??Promise是異步編程的一種解決方案俘闯,比傳統(tǒng)的解決方案——回調(diào)函數(shù)和事件——更合理和...
    呼呼哥閱讀 2,170評論 0 16
  • 00真朗、前言Promise 是異步編程的一種解決方案此疹,比傳統(tǒng)的解決方案——回調(diào)函數(shù)和事件——更合理和更強大。它由社區(qū)...
    夜幕小草閱讀 2,133評論 0 12
  • //本文內(nèi)容起初摘抄于 阮一峰 作者的譯文遮婶,用于記錄和學習蝗碎,建議觀者移步于原文 概念: 所謂的Promise,...
    曾經(jīng)過往閱讀 1,238評論 0 7
  • 這個夏天來的嬌柔做作卻也不失清新自然旗扑。晚安~
    Gigi熊閱讀 239評論 0 1