ECMAScript 6 入門筆記(一)—— Promise對象

14.1 含義

Promise保存著一個異步操作的結(jié)果

特點

  • 對象的狀態(tài)不受外界影響
  • 一旦狀態(tài)改變,就不會再變扒披,任何時候都可以得到這個結(jié)果。

缺點

  • 無法取消Promise,一旦新建它就會立即執(zhí)行路狮,無法中途取消(優(yōu)點就是不受外界因素影響)
  • 如果不設(shè)置回調(diào)函數(shù)步绸,Promise內(nèi)部拋出的錯誤尝丐,不會反應(yīng)到外部
  • 當(dāng)處于pending狀態(tài)時,無法得知目前進(jìn)展到哪一個階段(剛剛開始還是即將完成)宋税。

14.2 基本用法

ES6 規(guī)定摊崭,Promise對象是一個構(gòu)造函數(shù),用來生成Promise實例杰赛。

// Promise構(gòu)造函數(shù)接受一個函數(shù)作為參數(shù)呢簸,
// 該函數(shù)的兩個參數(shù)分別是resolve和reject。它們是兩個函數(shù)乏屯,
// 由 JavaScript 引擎提供根时,不用自己部署。
const promise = new Promise(function(resolve, reject) {
  // ... some code

  if (/* 異步操作成功 */){
    resolve(value); 
  } else {
    reject(error);
  }
});

Promise實例生成以后辰晕,可以用then方法分別指定resolved狀態(tài)(成功)和rejected狀態(tài)(失敗)的回調(diào)函數(shù)蛤迎。
參數(shù)二可選,這兩個函數(shù)都接受Promise對象傳出的值作為參數(shù)含友。

promise.then(function(value) {
  // success
}, function(error) {
  // failure
});

異步加載圖片例子

function loadImageAsync(url) {
  return new Promise(function(resolve, reject) {
    const image = new Image();

    image.onload = function() {
      resolve(image);
    };

    image.onerror = function() {
      reject(new Error('Could not load image at ' + url));
    };

    image.src = url;
  });
}

Ajax 操作的例子替裆。

const getJSON = function(url) {
  const promise = new Promise(function(resolve, reject){
    const handler = function() {
      if (this.readyState !== 4) {
        return;
      }
      if (this.status === 200) {
        resolve(this.response);
      } else {
        reject(new Error(this.statusText));
      }
    };
    const client = new XMLHttpRequest();
    client.open("GET", url);
    client.onreadystatechange = handler;
    client.responseType = "json";
    client.setRequestHeader("Accept", "application/json");
    client.send();

  });

  return promise;
};

getJSON("/posts.json").then(function(json) {
  console.log('Contents: ' + json);
}, function(error) {
  console.error('出錯了', error);
});

如果調(diào)用resolve函數(shù)和reject函數(shù)時帶有參數(shù),那么它們的參數(shù)會被傳遞回調(diào)函數(shù)

reject函數(shù)的參數(shù)通常是Error對象實例
resolve函數(shù)的參數(shù)除正常值外還可以是Promise實例

14.3 Promise.prototype.then()

作用:

為 Promise 實例添加狀態(tài)改變時的回調(diào)函數(shù)窘问。

參數(shù):

.then(參數(shù)1, 參數(shù)2)辆童, then方法的第一個參數(shù)是resolved狀態(tài)的回調(diào)函數(shù),第二個參數(shù)(可選)是rejected狀態(tài)的回調(diào)函數(shù)惠赫。
(理解成: 一個成功回調(diào)函數(shù)把鉴,一個失敗回調(diào)函數(shù),而失敗回調(diào)函數(shù)是可選的)

返回值:

一個新的Promise實例(注意儿咱,不是原來那個Promise實例)庭砍。因此可以采用鏈?zhǔn)綄懛ǎ磘hen方法后面再調(diào)用另一個then方法混埠。

getJSON("/posts.json").then(function(json) {
  return json.post;
}).then(function(post) {
  // ...
});
//這里應(yīng)該有三個Promise實例
//getJSON本身是一個怠缸,返回json.post是一個,第二個then方法也是一個Promise實例

code

getJSON("/post/1.json").then(function(post) {
  return getJSON(post.commentURL);
}).then(function funcA(comments) {
  console.log("resolved: ", comments);
}, function funcB(err){
  console.log("rejected: ", err);
});

用箭頭函數(shù)寫

getJSON("/post/1.json").then(
  post => getJSON(post.commentURL);
).then(
  comments => console.log("resolved: ", comments);
, err => console.log("rejected: ", err);
);

//箭頭函數(shù)()=>{}     (實為一個表達(dá)式钳宪,而非函數(shù))

function foo (a) {
    console.log(a);
}
//變成箭頭就是
const foo = (a) => {console.log(a)};  //return一個表達(dá)式的時候才能直接省略在箭頭后面

function foo (a,b) {
    let total = a + b;
    console.log(total);
}
const foo = (a, b) => {
    let total = a + b;
    console.log(total);
}

14.4 Promise.prototype.catch()

返回值: Promise 對象

Promise.prototype.catch方法是.then(null, rejection)的別名凯旭,用于指定發(fā)生錯誤時的回調(diào)函數(shù)。

getJSON('/posts.json').then(function(posts) {
  // ...
}).catch(function(error) {
  // 處理 getJSON 和 前一個回調(diào)函數(shù)運行時發(fā)生的錯誤
  console.log('發(fā)生錯誤使套!', error);
});

舉個栗子

const promise = new Promise(function(resolve, reject) {
  throw new Error('test');
});
promise.catch(function(error) {
  console.log(error);
});
// Error: test

如果 Promise 狀態(tài)已經(jīng)變成resolved罐呼,再拋出錯誤是無效的。

const promise = new Promise(function(resolve, reject) {
  resolve('ok');
  throw new Error('test');
});
promise
  .then(function(value) { console.log(value) })
  .catch(function(error) { console.log(error) });
// ok

Promise 對象的錯誤具有“冒泡”性質(zhì)侦高,會一直向后傳遞嫉柴,直到被捕獲為止。也就是說奉呛,錯誤總是會被下一個catch語句捕獲计螺。

getJSON('/post/1.json').then(function(post) {
  return getJSON(post.commentURL);
}).then(function(comments) {
  // some code
}).catch(function(error) {
  // 處理前面三個Promise產(chǎn)生的錯誤
});
// bad
promise
  .then(function(data) {
    // success
  }, function(err) { //不要在then中定義reject回調(diào)夯尽,
    // error
  });

// good
promise
  .then(function(data) { //cb
    // success
  })
  .catch(function(err) { //建議總是使用catch
    // error
  });

catch方法之中,還能再拋出錯誤登馒。

14.5 Promise.all()

作用

Promise.all方法用于將多個 Promise 實例匙握,包裝成一個新的 Promise 實例。

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

上面代碼中陈轿,Promise.all方法接受一個數(shù)組作為參數(shù)圈纺,p1、p2麦射、p3都是 Promise 實例蛾娶,如果不是,就會先調(diào)用下面講到的Promise.resolve方法潜秋,將參數(shù)轉(zhuǎn)為 Promise 實例蛔琅,再進(jìn)一步處理。(Promise.all方法的參數(shù)可以不是數(shù)組峻呛,但必須具有 Iterator 接口罗售,且返回的每個成員都是 Promise 實例。)

p的狀態(tài)由p1钩述、p2寨躁、p3決定,分成兩種情況切距。

(1)只有p1朽缎、p2惨远、p3的狀態(tài)都變成fulfilled谜悟,p的狀態(tài)才會變成fulfilled,此時p1北秽、p2葡幸、p3的返回值組成一個數(shù)組,傳遞給p的回調(diào)函數(shù)贺氓。

(2)只要p1蔚叨、p2、p3之中有一個被rejected辙培,p的狀態(tài)就變成rejected蔑水,此時第一個被reject的實例的返回值,會傳遞給p的回調(diào)函數(shù)扬蕊。

p的狀態(tài)簡單的說就是

p1 && p2 && p3 為true時搀别,結(jié)果才為true,否則false

// 生成一個Promise對象的數(shù)組
const promises = [2, 3, 5, 7, 11, 13].map(function (id) {
  return getJSON('/post/' + id + ".json");
});

Promise.all(promises).then(function (posts) {
  // ...
}).catch(function(reason){
  // ...
});

14.6 Promise.race()

Promise.race方法同樣是將多個 Promise 實例尾抑,包裝成一個新的 Promise 實例歇父。

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

上面代碼中蒂培,只要p1、p2榜苫、p3之中有一個實例率先改變狀態(tài)护戳,p的狀態(tài)就跟著改變。那個率先改變的 Promise 實例的返回值垂睬,就傳遞給p的回調(diào)函數(shù)媳荒。

Promise.race方法的參數(shù)與Promise.all方法一樣,如果不是 Promise 實例羔飞,就會先調(diào)用下面講到的Promise.resolve方法肺樟,將參數(shù)轉(zhuǎn)為 Promise 實例,再進(jìn)一步處理逻淌。

14.7 Promise.resolve()

作用:

將現(xiàn)有對象轉(zhuǎn)為 Promise 對象

const jsPromise = Promise.resolve($.ajax('/whatever.json'));

Promise.resolve方法的參數(shù)分成四種情況么伯。

(1)參數(shù)是一個 Promise 實例

如果參數(shù)是 Promise 實例,那么Promise.resolve將不做任何修改卡儒、原封不動地返回這個實例田柔。

(2)參數(shù)是一個thenable對象

thenable對象指的是具有then方法的對象,比如下面這個對象

let thenable = {
  then: function(resolve, reject) {
    resolve(42);
  }
};

Promise.resolve方法會將這個對象轉(zhuǎn)為 Promise 對象骨望,然后就立即執(zhí)行thenable對象的then方法硬爆。

let thenable = {
  then: function(resolve, reject) {
    resolve(42);
  }
};

let p1 = Promise.resolve(thenable);
p1.then(function(value) {
  console.log(value);  // 42
});

上面代碼中,thenable對象的then方法執(zhí)行后擎鸠,對象p1的狀態(tài)就變?yōu)閞esolved缀磕,從而立即執(zhí)行最后那個then方法指定的回調(diào)函數(shù),輸出 42劣光。

(3)參數(shù)不是具有then方法的對象袜蚕,或根本就不是對象

如果參數(shù)是一個原始值,或者是一個不具有then方法的對象绢涡,則Promise.resolve方法返回一個新的 Promise 對象牲剃,狀態(tài)為resolved。

const p = Promise.resolve('Hello');

p.then(function (s){
  console.log(s)
});
// Hello

上面代碼生成一個新的 Promise 對象的實例p雄可。由于字符串Hello不屬于異步操作(判斷方法是字符串對象不具有 then 方法)凿傅,返回 Promise 實例的狀態(tài)從一生成就是resolved,所以回調(diào)函數(shù)會立即執(zhí)行数苫。Promise.resolve方法的參數(shù)聪舒,會同時傳給回調(diào)函數(shù)。

(4)不帶有任何參數(shù)

Promise.resolve方法允許調(diào)用時不帶參數(shù)虐急,直接返回一個resolved狀態(tài)的 Promise 對象舒裤。

所以绢彤,如果希望得到一個 Promise 對象亲桦,比較方便的方法就是直接調(diào)用Promise.resolve方法届腐。

const p = Promise.resolve();

p.then(function () {
  // ...
});

上面代碼的變量p就是一個 Promise 對象。

需要注意的是,立即resolve的 Promise 對象,是在本輪“事件循環(huán)”(event loop)的結(jié)束時,而不是在下一輪“事件循環(huán)”的開始時间涵。

事件循環(huán)是?榜揖?勾哩?

setTimeout(function () {
  console.log('three');
}, 0);

Promise.resolve().then(function () {
  console.log('two');
});

console.log('one');

// one
// two
// three

上面代碼中,setTimeout(fn, 0)在下一輪“事件循環(huán)”開始時執(zhí)行举哟,Promise.resolve()在本輪“事件循環(huán)”結(jié)束時執(zhí)行思劳,console.log('one')則是立即執(zhí)行,因此最先輸出妨猩。



14.8 Promise.reject()

Promise.reject(reason)方法也會返回一個新的 Promise 實例潜叛,該實例的狀態(tài)為rejected。

const p = Promise.reject('出錯了');
// 等同于
const p = new Promise((resolve, reject) => reject('出錯了'))

p.then(null, function (s) {
  console.log(s)
});
// 出錯了

注意:
Promise.reject()方法的參數(shù)壶硅,會原封不動地作為reject的理由(理由威兜? 啥意思),變成后續(xù)方法的參數(shù)庐椒。這一點與Promise.resolve方法不一致椒舵。

const thenable = {
  then(resolve, reject) {
    reject('出錯了');
  }
};

Promise.reject(thenable)
.catch(e => {
  console.log(e === thenable)
})
// true

上面代碼中,Promise.reject方法的參數(shù)是一個thenable對象约谈,執(zhí)行以后笔宿,后面catch方法的參數(shù)不是reject拋出的“出錯了”這個字符串,而是thenable對象棱诱。

一句話: 出錯就拋出原Promise

14.9 兩個有用的附加方法(不在ES6之中)

done()

Promise 對象的回調(diào)鏈泼橘,不管以then方法或catch方法結(jié)尾,要是最后一個方法拋出錯誤军俊,都有可能無法捕捉到(因為 Promise 內(nèi)部的錯誤不會冒泡到全局)侥加。因此捧存,我們可以提供一個done方法粪躬,總是處于回調(diào)鏈的尾端,保證拋出任何可能出現(xiàn)的錯誤昔穴。

asyncFunc()
  .then(f1)
  .catch(r1)
  .then(f2)
  .done();
  

它的實現(xiàn)代碼相當(dāng)簡單镰官。

Promise.prototype.done = function (onFulfilled, onRejected) {
  this.then(onFulfilled, onRejected)
    .catch(function (reason) {
      // 拋出一個全局錯誤
      setTimeout(() => { throw reason }, 0);
    });
};

從上面代碼可見,done方法的使用吗货,可以像then方法那樣用泳唠,提供fulfilled和rejected狀態(tài)的回調(diào)函數(shù),也可以不提供任何參數(shù)宙搬。但不管怎樣笨腥,done都會捕捉到任何可能出現(xiàn)的錯誤拓哺,并向全局拋出。

finally()

finally方法用于指定不管 Promise 對象最后狀態(tài)如何脖母,都會執(zhí)行的操作士鸥。它與done方法的最大區(qū)別,它接受一個普通的回調(diào)函數(shù)作為參數(shù)谆级,該函數(shù)不管怎樣都必須執(zhí)行烤礁。

下面是一個例子,服務(wù)器使用 Promise 處理請求肥照,然后使用finally方法關(guān)掉服務(wù)器脚仔。

server.listen(0)
  .then(function () {
    // run test
  })
  .finally(server.stop);

它的實現(xiàn)也很簡單。

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

上面代碼中舆绎,不管前面的 Promise 是fulfilled還是rejected鲤脏,都會執(zhí)行回調(diào)函數(shù)callback。

14.10 應(yīng)用

詳情見: http://es6.ruanyifeng.com/#docs/promise#應(yīng)用

14.11 Promise.try()

詳情見: http://es6.ruanyifeng.com/#docs/promise#Promise-try

阮老師 ES6標(biāo)準(zhǔn)入門

http://es6.ruanyifeng.com/#docs/promise

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末吕朵,一起剝皮案震驚了整個濱河市凑兰,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌边锁,老刑警劉巖姑食,帶你破解...
    沈念sama閱讀 211,194評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異茅坛,居然都是意外死亡音半,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,058評論 2 385
  • 文/潘曉璐 我一進(jìn)店門贡蓖,熙熙樓的掌柜王于貴愁眉苦臉地迎上來曹鸠,“玉大人,你說我怎么就攤上這事斥铺〕固遥” “怎么了?”我有些...
    開封第一講書人閱讀 156,780評論 0 346
  • 文/不壞的土叔 我叫張陵晾蜘,是天一觀的道長邻眷。 經(jīng)常有香客問我,道長剔交,這世上最難降的妖魔是什么肆饶? 我笑而不...
    開封第一講書人閱讀 56,388評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮岖常,結(jié)果婚禮上驯镊,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好板惑,可當(dāng)我...
    茶點故事閱讀 65,430評論 5 384
  • 文/花漫 我一把揭開白布橄镜。 她就那樣靜靜地躺著,像睡著了一般冯乘。 火紅的嫁衣襯著肌膚如雪蛉鹿。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,764評論 1 290
  • 那天往湿,我揣著相機與錄音妖异,去河邊找鬼。 笑死领追,一個胖子當(dāng)著我的面吹牛他膳,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播绒窑,決...
    沈念sama閱讀 38,907評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼棕孙,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了些膨?” 一聲冷哼從身側(cè)響起蟀俊,我...
    開封第一講書人閱讀 37,679評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎订雾,沒想到半個月后肢预,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,122評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡洼哎,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,459評論 2 325
  • 正文 我和宋清朗相戀三年烫映,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片噩峦。...
    茶點故事閱讀 38,605評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡锭沟,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出识补,到底是詐尸還是另有隱情族淮,我是刑警寧澤,帶...
    沈念sama閱讀 34,270評論 4 329
  • 正文 年R本政府宣布凭涂,位于F島的核電站祝辣,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏导盅。R本人自食惡果不足惜较幌,卻給世界環(huán)境...
    茶點故事閱讀 39,867評論 3 312
  • 文/蒙蒙 一揍瑟、第九天 我趴在偏房一處隱蔽的房頂上張望白翻。 院中可真熱鬧,春花似錦、人聲如沸滤馍。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,734評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽巢株。三九已至槐瑞,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間阁苞,已是汗流浹背困檩。 一陣腳步聲響...
    開封第一講書人閱讀 31,961評論 1 265
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留那槽,地道東北人悼沿。 一個月前我還...
    沈念sama閱讀 46,297評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像骚灸,于是被迫代替她去往敵國和親糟趾。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,472評論 2 348

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

  • 00、前言Promise 是異步編程的一種解決方案丈钙,比傳統(tǒng)的解決方案——回調(diào)函數(shù)和事件——更合理和更強大非驮。它由社區(qū)...
    夜幕小草閱讀 2,128評論 0 12
  • Promiese 簡單說就是一個容器,里面保存著某個未來才會結(jié)束的事件(通常是一個異步操作)的結(jié)果雏赦,語法上說院尔,Pr...
    雨飛飛雨閱讀 3,352評論 0 19
  • 本文適用的讀者 本文寫給有一定Promise使用經(jīng)驗的人,如果你還沒有使用過Promise喉誊,這篇文章可能不適合你邀摆,...
    HZ充電大喵閱讀 7,299評論 6 19
  • 遇見你是生命中最柔軟的時光 每個人心中,都有那么一席之地伍茄。是留給最心愛的人栋盹。即使這一季的銀杏樹又黃了。你未來敷矫?我怎...
    意玲閱讀 283評論 0 1
  • 今天晚上是永澄老師第三次領(lǐng)讀《好好學(xué)習(xí)》例获,依然覺得我和老師讀的不是同一本書。那么我在老師今晚的領(lǐng)讀中學(xué)習(xí)...
    楊燕君閱讀 391評論 0 2