摘要:構造函數規定,對象是一個構造函數,用來生成實例。如果中的回調函數拋出一個錯誤,那么返回的將會成為拒絕狀態,并且將拋出的錯誤作為拒絕狀態的回調函數的參數值。
其實想寫 Promise 的使用已經很長時間了。一個是在實際編碼的過程中經常用到,一個是確實有時候小伙伴們在使用時也會遇到一些問題。前言 && 基礎概念
Promise 也確實是 ES6 中 對于寫 JS 的方式,有著真正最大影響的 API 特性之一。
本文是實際使用使用過程中的一個總結
看一下文件創建時間 2017-10-09,拖延癥真是太可怕了。。。還是得增強執行力啊!不忘初心,加油吧!
博客原址
Promise 是解決 JS 異步的一種方案,相比傳統的回調函數,Promise 能解決多個回調嚴重嵌套的問題。
Promise 對象代表一個異步操作,有三種狀態: pending、fulfilled 或 rejected ,狀態的轉變只能是 pending -> fulfilled 或者 pending -> rejected ,且這個過程一旦發生就不可逆轉。
Promise 構造函數個人認為講解 Promise 實際上需要分成兩個部分
對于 Promise 構造函數的使用說明。
Promise 原型對象上的一些方法。
ES6 規定,Promise 對象是一個構造函數,用來生成 Promise 實例。
Promise 構造函數接受一個函數作為參數,該函數的兩個參數分別是 resolve 和 reject 。它們是兩個函數,由 JavaScript 引擎提供,不用自己部署。
resolve 函數的作用是將 Promise 對象的狀態從“未完成”變為“成功”(即從 pending 變為 fulfilled ),在異步操作成功時調用,并將異步操作的結果,作為參數傳遞出去;
reject 函數的作用是,將 Promise 對象的狀態從“未完成”變為“失敗”(即從 pending 變為 rejected ),在異步操作失敗時調用,并將異步操作報出的錯誤,作為參數傳遞出去。
下面代碼創造了一個 Promise 實例。
function request() { return new Promise((resolve, reject) => { /* 異步操作成功 */ setTimeout(() => { resolve("success"); }, 1000); // 取消注釋這里可以體現,Promise 的狀態一旦變更就不會再變化的特性 // reject("error"); }); }
接收
request() .then(result => { console.info(result); }) .catch(error => { console.info(error); });
上述 new Promise() 之后,除去用 catch 去捕獲錯誤之外,也可以用 then 方法指定 resolve 和 reject 的回調函數
也能達到捕獲錯誤的目的。
request().then( result => { console.info(result); }, error => { console.info(error); } );
原型上的方法
Promise.prototype.then()p.then(onFulfilled, onRejected);
then 方法 是定義在 Promise.prototype 上的方法,如上面的例子一樣,有兩個參數,fulfilled 的回調函數和 rejected 的回調函數,第二個參數時可選的。
兩個關鍵點:
then 方法的返回值是一個新的 Promise 實例,所以對于調用者而言,拿到一個 Promise 對象,調用 then 后仍然返回一個 Promise ,而它的行為與 then 中的回調函數的返回值有關。如下:
如果 then 中的回調函數返回一個值,那么 then 返回的 Promise 將會成為接受狀態,并且將返回的值作為接受狀態的回調函數的參數值。
如果 then 中的回調函數拋出一個錯誤,那么 then 返回的 Promise 將會成為拒絕狀態,并且將拋出的錯誤作為拒絕狀態的回調函數的參數值。
如果 then 中的回調函數返回一個已經是接受狀態的 Promise,那么 then 返回的 Promise 也會成為接受狀態,并且將那個 Promise 的接受狀態的回調函數的參數值作為該被返回的 Promise 的接受狀態回調函數的參數值。
如果 then 中的回調函數返回一個已經是拒絕狀態的 Promise,那么 then 返回的 Promise 也會成為拒絕狀態,并且將那個 Promise 的拒絕狀態的回調函數的參數值作為該被返回的 Promise 的拒絕狀態回調函數的參數值。
如果 then 中的回調函數返回一個未定狀態(pending)的 Promise,那么 then 返回 Promise 的狀態也是未定的,并且它的終態與那個 Promise 的終態相同;同時,它變為終態時調用的回調函數參數與那個 Promise 變為終態時的回調函數的參數是相同的。
鏈式調用。把嵌套回調的代碼格式轉換成一種鏈式調用的縱向模式。
比如說回調形式: 一個回調地獄的例子
a(a1 => { b(a1, b1 => { c(b1, c1 => { d(c1, d1 => { console.log(d1); }); }); }); });
這樣的橫向擴展可以修改成(a,b,c,d)均為返回 Promise 的函數
a() .then(b) .then(c) .then(d) .then(d1 => { console.log(d1); }); //===== 可能上面的例子并不太好看 ===下面這樣更直觀 a() .then(a1 => b(a1)) .then(b1 => c(b1)) .then(c1 => d(c1)) .then(d1 => { console.log(d1); });
這樣的縱向結構,看上去清爽多了。
Promise.prototype.catch()除了 then() ,在 Promise.prototype 原型鏈上的還有 catch() 方法,這個是拒絕的情況的處理函數。
其實 它的行為與調用 Promise.prototype.then(undefined, onRejected) 相同。 (事實上, calling obj.catch(onRejected) 內部 calls obj.then(undefined, onRejected)).
// 1. request().then( result => { console.info(result); }, error => { console.info(error); } ); // 2. request() .then(result => { console.info(result); }) .catch(error => { console.info(error); });
如上這個例子:兩種方式在使用,與結果基本上是等價的,但是 仍然推薦第二種寫法,下面我會給出原因:
在 Promise 鏈中 Promise.prototype.then(undefined, onRejected),onRejected 方法無法捕獲當前 Promise 拋出的錯誤,而后續的 .catch 可以捕獲之前的錯誤。
代碼冗余
new Promise((resolve, reject) => { setTimeout(() => { resolve("reject"); }, 1000); }) .then( result => { console.log(result + "1"); throw Error(result + "1"); // 拋出一個錯誤 }, error => { console.log(error + ":1"); // 不會走到這里 } ) .then( result => { console.log(result + "2"); return Promise.resolve(result + "2"); }, error => { console.log(error + ":2"); } ); // reject1, Error: reject1:2
如果使用 .catch 方法,代碼會簡化很多,這樣實際上是延長了 Promise 鏈
new Promise((resolve, reject) => { setTimeout(() => { resolve("reject"); }, 1000); }) .then(result => { console.log(result + "1"); throw Error(result + "1"); // 拋出一個錯誤 }) .then(result => { console.log(result + "2"); return Promise.resolve(result + "2"); }) .catch(err => { console.log(err); }); // reject1, Error: reject1:2Promise.prototype.finally()
暫未完全成為標準的一部分,處于:Stage 4
finally() 方法返回一個 Promise,在執行 then() 和 catch() 后,都會執行finally指定的回調函數。(回調函數中無參數,僅僅代表 Promise 的已經結束
等同于使用 .then + .catch 延長了原有的 Promise 鏈的效果,避免同樣的語句需要在 then() 和 catch() 中各寫一次的情況。
mdn-Promise-finally
Promise 對象上的方法 Promise.all() 用來處理 Promise 的并發Promise.all 會將多個 Promise 實例封裝成一個新的 Promise 實例,新的 promise 的狀態取決于多個 Promise 實例的狀態,只有在全體 Promise 都為 fulfilled 的情況下,新的實例才會變成 fulfilled 狀態。;如果參數中 Promise 有一個失敗(rejected),此實例回調失敗(rejecte),失敗原因的是第一個失敗 Promise 的結果。
舉個例子:
Promise.all([ new Promise(resolve => { setTimeout(resolve, 1000, "p1"); }), new Promise(resolve => { setTimeout(resolve, 2000, "p2"); }), new Promise(resolve => { setTimeout(resolve, 3000, "p3"); }) ]) .then(result => { console.info("then", result); }) .catch(error => { console.info("catch", error); }); // [p1,p2,p3] Promise.all([ new Promise(resolve => { setTimeout(resolve, 1000, "p1"); }), new Promise(resolve => { setTimeout(resolve, 2000, "p2"); }), Promise.reject("p3 error") ]) .then(result => { console.info("then", result); }) .catch(error => { console.info("catch", error); }); // p3 error
獲取 cnode 社區的 精華貼的前十條內容
fetch("https://cnodejs.org/api/v1/topics?tab=good&limit=10") .then(res => res.json()) .then(res => { const fetchList = res.data.map(item => { return fetch(`https://cnodejs.org/api/v1/topic/${item.id}`) .then(res => res.json()) .then(res => res.data); }); Promise.all(fetchList).then(list => { console.log(list); }); });Promise.race() 競態執行
Promise.race 也會將多個 Promise 實例封裝成一個新的Promise實例,只不過新的 Promise 的狀態取決于最先改變狀態的 Promise 實例的狀態。
在前端最典型的一個用法是為 fetch api 模擬請求超時。
Promise.race([ fetch("https://cnodejs.org/api/v1/topics?tab=good&limit=10").then(res => res.json() ), new Promise((resolve, reject) => { setTimeout(reject, 1, "error"); }) ]) .then(result => { console.info("then", result); }) .catch(error => { console.info("catch", error); // 進入這里 });
上述例子中只要請求 未在 1 毫秒內結束就會進入 .catch() 方法中,雖然不能將請求取消,但是超時模擬卻成功了
Promise.resolve(value) && Promise.reject(reason)這兩個方法都能用來創建并返回一個新的 Promise , 區別是 Promise.resolve(value) 攜帶進新的 Promise 狀態是 fulfilled。而 Promise.reject(reason) 帶來的 rejected
有的時候可以用來簡化一些創建 Promise 的操作如:
const sleep = (time = 0) => new Promise(resolve => setTimeout(resolve, time)); // 這里創建一個 睡眠,并且打印的鏈 Promise.resolve() .then(() => { console.log(1); }) .then(() => sleep(1000)) .then(() => { console.log(2); }) .then(() => sleep(2000)) .then(() => { console.log(3); });
有時也用來 手動改變 Promise 鏈中的返回狀態 ,當然這樣實際上和 直接返回一個值,或者是 使用 throw Error 來構造一個錯誤,并無區別。到底要怎么用 就看個人喜好了
new Promise((resolve, reject) => { setTimeout(() => { resolve("resolve"); // 1. }, 1000); }) .then(result => { return Promise.reject("reject1"); // 2. }) .then( result => { return Promise.resolve(result + "2"); }, err => { return Promise.resolve(err); // 3. } ) .then(res => { console.log(res); // 4. }) .catch(err => { console.log(err + "err"); }); // reject1幾個例子
下面來看幾個例子:
關于執行順序,具體可搜索,js 循環
new Promise((resolve, reject) => { console.log("step 1"); resolve(); console.log("step 2"); }).then(() => { console.log("step 3"); }); console.log("step 4"); // step 1, step 2, step 4 , step 3
在使用 Promise 構造函數構造 一個 Promise 時,回調函數中的內容就會立即執行,而 Promise.then 中的函數是異步執行的。
關于狀態不可變更
let start; const p = new Promise((resolve, reject) => { setTimeout(() => { start = Date.now(); console.log("once"); resolve("success"); }, 1000); }); p.then(res => { console.log(res, Date.now() - start); }); p.then(res => { console.log(res, Date.now() - start); }); p.then(res => { console.log(res, Date.now() - start); });
Promise 構造函數只執行一次,內部狀態一旦改變,有了一個值,后續不論調用多少次then()都只拿到那么一個結果。
關于好像狀態可以變更
const p1 = new Promise((resolve, reject) => { setTimeout(() => { resolve("success"); }, 1000); }); const p2 = p1.then((resolve, reject) => { throw new Error("error"); }); console.log("p1", p1); console.log("p2", p2); setTimeout(() => { console.log("p1", p1); console.log("p2", p2); }, 2000);
觀察這一次的打印
第一次打印出兩個 Promise 的時候都是 pending ,因為 p2 是基于 p1 的結果,p1 正在 pending ,立即打印出的時候肯定是 pending ;第二次打印的時候,因為 p1 的狀態為 resolved ,p2 為 rejected ,這個并不是已經為 fulfilled 狀態改變為 rejected ,而是 p2 是一個新的 Promise 實例,then() 返回新的 Promise 實例。
關于透傳
Promise.resolve(11) .then(1) .then(2) .then(3) .then(res => { console.info("res", res); }); // 11
給 then 方法傳遞了一個非函數的值,等同于 then(null),會導致穿透的效果,就是直接過掉了這個 then() ,直到符合規范的 then() 為止。
Promise 的串行調用使用 Array.reduce 方法串行執行 Promise
const sleep = (time = 0) => new Promise(resolve => setTimeout(resolve, time)); [1000, 2000, 3000, 4000].reduce((Promise, item, index) => { return Promise.then(res => { console.log(index + 1); return sleep(item); }); }, Promise.resolve()); // 在分別的等待時間后輸出 1,2,3,4
這篇文章到這里就基本上結束了,相信 如果能理解上面的內容,并且在實際項目中使用的話。應該會讓工作更高效吧,對于新的異步使用應該也會更加的得心應手。Promise 的使用相對簡單,可能后續再出一篇如何實現一個 Promise 吧
那些收集的 Promise 的優質文章。
bluebird 是一個拓展 Promise 方法的庫,提供了非常多的實用的方法,推薦
[[思維導圖] Promise - 《你不知道的 JavaScript》- 中卷 - 第二部分](https://zhuanlan.zhihu.com/p/...
[[譯] 一個簡單的 ES6 Promise 指南](https://zhuanlan.zhihu.com/p/...
阮一峰-ES6 入門 Promise 對象
Promise 不夠中立
WHY “PROMISES ARE NOT NEUTRAL ENOUGH” IS NOT NEUTRAL ENOUGH
【譯】關于 Promise 的 9 個提示
Promise 必知必會(十道題)
Event Loop 必知必會(六道題)
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/98434.html
摘要:從最開始的到封裝后的都在試圖解決異步編程過程中的問題。為了讓編程更美好,我們就需要引入來降低異步編程的復雜性。寫一個符合規范并可配合使用的寫一個符合規范并可配合使用的理解的工作原理采用回調函數來處理異步編程。 JavaScript怎么使用循環代替(異步)遞歸 問題描述 在開發過程中,遇到一個需求:在系統初始化時通過http獲取一個第三方服務器端的列表,第三方服務器提供了一個接口,可通過...
摘要:從最開始的到封裝后的都在試圖解決異步編程過程中的問題。為了讓編程更美好,我們就需要引入來降低異步編程的復雜性。異步編程入門的全稱是前端經典面試題從輸入到頁面加載發生了什么這是一篇開發的科普類文章,涉及到優化等多個方面。 TypeScript 入門教程 從 JavaScript 程序員的角度總結思考,循序漸進的理解 TypeScript。 網絡基礎知識之 HTTP 協議 詳細介紹 HTT...
摘要:感謝大神的免費的計算機編程類中文書籍收錄并推薦地址,以后在倉庫里更新地址,聲音版全文狼叔如何正確的學習簡介現在,越來越多的科技公司和開發者開始使用開發各種應用。 說明 2017-12-14 我發了一篇文章《沒用過Node.js,就別瞎逼逼》是因為有人在知乎上黑Node.js。那篇文章的反響還是相當不錯的,甚至連著名的hax賀老都很認同,下班時讀那篇文章,竟然坐車的還坐過站了。大家可以很...
摘要:感謝大神的免費的計算機編程類中文書籍收錄并推薦地址,以后在倉庫里更新地址,聲音版全文狼叔如何正確的學習簡介現在,越來越多的科技公司和開發者開始使用開發各種應用。 說明 2017-12-14 我發了一篇文章《沒用過Node.js,就別瞎逼逼》是因為有人在知乎上黑Node.js。那篇文章的反響還是相當不錯的,甚至連著名的hax賀老都很認同,下班時讀那篇文章,竟然坐車的還坐過站了。大家可以很...
閱讀 2596·2023-04-25 15:07
閱讀 705·2021-11-24 10:21
閱讀 2298·2021-09-22 10:02
閱讀 3517·2019-08-30 15:43
閱讀 3222·2019-08-30 13:03
閱讀 2287·2019-08-29 17:18
閱讀 3586·2019-08-29 17:07
閱讀 1873·2019-08-29 12:27