深入解析ES6中的promise
- 2019 年 11 月 29 日
- 筆記

file
作者 | Jeskson 來源 | 達達前端小酒館
什麼是Promise
Promise對象是用於表示一個異步操作的最終狀態(完成或失敗)以及其返回的值。
什麼是同步,異步
同步任務會阻塞程序的執行,如alert,for
異步任務不會阻塞程序的執行,如setTimeou
使用Promise,then,catch,finally
Promise.all 和 Promise.race
Promise.resolve 和 Promise.reject
回調與Promise
回調函數,用於請求數據
function backFunction(fn) { setTimeout(function() { fn && fn(); }, 1000); } // 調用 backFunction(function() { console.log(1); // 1 backFunction(function() { console.log(2); // 2 backFunction(function() { console.log(3); // 3 }); }); });
Promise
function d() { return new Promise(resolve = { setTimeout(function() { resolve(); // resolve成功的時候要做的事情 },1000); // 1秒後調用resolve(),它是一個函數 }) } d() .then(function() { console.log(1); return d(); // Promise實例 }) .then(function() { console.log(2); return d(); // Promise實例 }).then(function() { console.log(3); });
對比回調
// 動畫 function moveTo(el, x, y, fn) { el.style.transform = `translate(${x}px, ${y}px)`; setTimeout(function() { fn && fn(); },1000); } let el = document.querySelector('div'); document.querySelector('button').addeventListener('click', e moveTo(el, 100, 100, function() { console.log(1); moveTo(el, 200, 200, function() { console.log(2); }); }) });
// promise function moveTo(el,x,y) { return new Promise(resolve => { el.style.transform = `translate(${x}px, ${y}px)`; setTimeout(function() { resolve(); },1000); }); } document.querySelector('button').addEventListener('click', e=>{ moveTo(el,100,100) .then(function() { console.log('da'); return moveTo(el, 200, 200); }) .then(function() { console.log('da2'); }).then(function() { console.log('da2'); }); });
信任問題
// 使用第三方庫 回調 function method(fn) { // 回調 setTimeout(function() { // 回調 fn && fn(); // 有可以有bug,被多調用一次 fn && fn(); },1000); } // promise一旦被調用,成功或者是失敗後,就不能再被修改 function method() { return new Promise(resolve => { setTimeout(function() { //成功 resolve(); // 再調用就不會執行 resolve(); },1000); }); }
// 控制反轉 function method(fn) { setTimeout(function() { // 執行回調 fn && fn.call({a:1, b:2)}; // 改變指向 },1000); } function method(fn) { return new Promise(resolve => { setTimeout(() => { resolve(); },1000); }); }
錯誤處理
then(resolve, reject) then方法中的第二個回調,是失敗的時候要做的事情
catch 使用實例的then方法,可以捕獲錯誤
finally 不論成功與否,finally中的內容一定會執行
function fn(val) { return new Promise((resolve, reject) => { if(val) { resolve(); // 成功的時候 } else { reject(); // 失敗的時候 } }); }
fn(false) .then( () => { console.log("成功"); }, () => { console.log("失敗"); }) function fn(val) { return new Promise((resolve, reject) => { if(val) { resolve(); // 成功的時候 } else { reject('404'); // 失敗的時候 } }); } fn(false) .then( () => { console.log("成功"); }, e => { console.log(e); }) promise 中resolve只能傳遞一個參數,如下: function fn(val) { return new Promise((resolve, reject) => { if(val) { resolve({name: 'da'}); // 成功的時候 } else { reject('404'); // 失敗的時候 } }); } fn(true) .then( dada => { console.log(data); }, e => { console.log(e); })
catch會捕獲錯誤,如果在回調中沒有對錯誤進行處理
fn(true) .then(data => { console.log(data); return fn(false); }) .then( () => { console.log('da'); // 不會執行,沒處理錯誤 }) .then( () => { }) .catch(e => { console.log(e); return fn(false); }); // 直接輸出到這 不能保證catch被執行
如果沒有對失敗做出處理,會報錯
fn(true) .then(data => { console.log(data); return fn(false); }) .catch(e=> { // 捕獲錯誤 console.log(e); return fn(false); }) .finally( () => { console.log(100); });
Promise的三種狀態
pending為進行中的狀態,fulfilled為成功的狀態,rejected為失敗的狀態。狀態的改變時不可返的,一旦決議就不能修改(決議,狀態的改變為決議),狀態只能從pending到fulfilled,或者,從pending到rejected。
Promise.all方法可以把多個promise的實例包裝成一個新的promise實例
Promise.all( [promise1, promise2] ) : Promise 數組中,如果promise都為true,則返回為true,決議為成功 如果數組中有一個為promise,那麼返回的是false,決議為失敗 如果是一個空數組,那麼返回為true,決議為成功
模式多個請求的數據
function getData1() { return new Promise((resolve, reject) => { setTimeout( () => { console.log('第一條數據加載成功'); resolve('data1'); },1000); }); } function getData2() { return new Promise((resolve, reject) => { setTimeout( () => { console.log('第二條數據加載成功'); resolve('data2'); },1000); }); } function getData3() { return new Promise((resolve, reject) => { setTimeout( () => { console.log('第三條數據加載成功'); resolve('data3'); // 改為 reject('err') },1000); }); }
let p = Promise.all( [getData1(), getData2(), getData3()] ); p.then(arr => { console.log(arr); }); // 失敗 p.then(arr => { console.log(arr); }, e => { console.log(e); }); let p = Promise.all([]); // 決議為成功 p.then( () => { console.log(`da`); }, e => { console.log(e); });
第一條數據加載成功 第二條數據加載成功 第三條數據加載成功
不用Promise.all
let count = 0; function getData1() { setTimeout( () => { console.log('第一條數據加載成功'); count ++; func(); },1000); } function getData2() { setTimeout( () => { console.log('第二條數據加載成功'); count ++; func(); },1000); } function getData3() { setTimeout( () => { console.log('第三條數據加載成功'); count ++; func(); },1000); } function getData4() { setTimeout( () => { console.log('第四條數據加載成功'); count ++; func(); },1000); } // 寫一個方法: function func() { if(count < 4)return; console.log('全部拿到了'); }
調用
getData2(); getData3(); getData4();

file
let err = false; function getData1() { setTimeout( () => { console.log('第一條數據加載成功'); if(status) err = true; count ++; func(); },1000); } function func() { if(count < 4)return; console.log('全部拿到了'); if(err) { // ... } }
Promise.race()
Promise.race([promise1, promise2]) : Promise
let p = Promise.race([getData1(), getData2(),getData3()]); p.then (data=>{ console.log(data); })
let flag = false; function func(data) { if(flag) return flag = true; console.log(data); } function getData1() { setTimeout(()=>{ console.log("第一條數據加載成功"); func({name: 'da'}); },500); } function getData2() { setTimeout( () => { console.log("第二條數據加載成功"); func({name: 'dada'}); }, 1000); } getData1(); getData2(); 第一條數據加載成功 {name: 'da'} 第二條數據加載成功
Promise.resolve與Promise.reject
Promise.resolve() 與 Promise.reject()
// Promise.resolve 傳遞一個普通的值 let p1 = new Promise(resolve => { resolve('成功!'); }); let p2 = Promise.resolve('成功!'); // 傳遞一個promise實例 let pro = new Promise(resolve => { resolve('da'); }); let p = Promise.resolve(pro); p.then(data => void console.log(data));
let obj = { then (cb) { console.log('da'); da('dada'); }, method() { console.log('coding'); } } // 立即執行 Promise.resolve(obj).then(data => { console.log(data); });
Promise異步:
console.log(1); let p = new Promise(resolve => { console.log(2); resolve(); console.log(3); }); console.log(4); p.then(()=>{ console.log(5); }); console.log(6); // 123465
Promise改善了傳統回調造成的代碼難維護,控制反轉等問題,promise是異步的,如果all接收的是空數組,馬上會被決議為成功,如果race接受的是空數組,那麼會被永遠掛起,無限捕獲錯誤問題。
resove和reject方法:
如果接收的是普通的值,那麼就會立即決議為成功,並填充這個值,如果接收的是一個promise實例,那麼返回這個promise實例,如果接收的是個thenable對象,則會把它包裝成promise對象,並立即執行這個對象的then方法,reject會產生一個決議失敗的promise並直接傳遞值。
JavaScript/ES6 Promise
JavaScript的Promise代表一個操作的結果還沒有結果,就是如網絡請求操作,當我們從某個數據源獲取數據的時候,沒有辦法確定它什麼時候能夠返回,接受到響應。
Promise提供標準
doSomething() .then(doSomethingElse) .catch(handleError) .then(doMore) .then(doFinally) .catch(handleAnotherError)
創建Promise
一個Promise使用Promise構造器創建,接受兩個參數resolve,reject
var promise = new Promise( function(resolve, reject) { // new Promise resolve() reject() }
XMLHttpRequest的promise版本: function get(url) { return new Promise(function(resolve, reject) { var req = new XMLHttpRequest(); req.open('GET', url); req.onload = function() { if(req.status == 200) { resolve(req.response); }else{ reject(Error(req.statusText)); } }; req.onerror = function() { reject(Error("Network Error")); }; req.send(); }); }
使用Promise
get(url) .then(function(response) { },function(err) { })
處理錯誤:
get(url) .then(function(response){ }, undefined) .then(undefined, function(err){ })
get(url) .then(function(response){ }) .catch(function(err){ })
鏈式
get(url) .then(function(response){ response = JSON.parse(response); var secondURL = response.data.url return get(secondURL); }) .then(function(response){ response = JSON.parse(response); var thirdURL = response.data.url return get(thirdURL); }) .catch(function(err){ handleError(err); });
並行執行Promise
Promise.all()方法每個promise數組成為則決議為成功,如果數組中有任意一個promise為失敗則決議為失敗。
任務一,任務二,任務三,.then() -> success 任務成功
ES6
Promise對象用於表示一個異步操作的最終狀態,以及其返回的值。
語法:
new Promise(function (resolve, reject) { });
幾種狀態:
pending初始狀態,既不是成功,也不是失敗狀態;fulfilled意味着操作成功完成;rejected意味着操作失敗。
pending狀態的Promise對象可能會觸發filfilled狀態,並傳遞一個值給響應的狀態處理方法,也可能觸發失敗狀態rejected並傳遞失敗信息。
Promise.all(iterable)
這個方法返回一個新的promise對象,該promise對象在itearable參數中,當裏面所有的的promise對象決議成功的時候才觸發成功,否則裏面如何一個promise對象決議失敗的時候,立即觸發promise對象的失敗。
Promise.all方法常用於處理多個promise對象的狀態集合。
Promise.race(iterable)
當iterable參數里的任意一個子promise被決議成功或者是決議失敗後,父promise會用子promise的成功返回值,或是失敗返回。
Promise.reject(reason)
返回一個狀態為失敗的promise對象,將給定的失敗信息傳遞給對應的處理方法。
Promise.resolve(value)
返回一個狀態為失敗的promise對象,將給定的失敗信息傳遞給對應的處理方法。
const myPromise = new Promise( (resolve, reject) => { resolve('resolve'); // filfilled reject('reject'); // rejected });
function myFunction(url) { return new Promise( (resolve, reject) => { xhr.open ("GET", url); xhr.onload = () => resolve(xhr.responseText); xhr.onerror = () => reject(xhr.statusText); xhr.send(); }); };
當異步代碼執行成功的時候,會調用resolve(),當異步代碼執行失敗的時候,會調用reject()。
模擬異步代碼:
setTimeout(function(){ resolve('成功'); },250); }); myPromise.then(function(successMsg){ });
ES6 Promise對象
Promise對象是異步編程的一種解決方案,語法上,Promise是一個對象,從它那可以獲取異步操作的信息。
Promise的狀態,promise異步操作有三種狀態,pending(進行中),fulfilled(已成功),reject(已失敗)。除了異步操作的結果,任何其他操作都是無法改變這個狀態。
const p1 = new Promise(function(resolve,reject){ resolve('success1'); resolve('success2'); }); const p2 = new Promise(function(resolve,reject){ resolve('success3'); reject('reject'); }); p1.then(function(value){ console.log(value); // success1 }); p2.then(function(value){ console.log(value); // success3 });
缺點,一旦建立Promise就會立即執行,無法中途取消,如果不設置回調函數,Promise內部會拋出錯誤,不會反應到外部。
then方法,接收兩個函數作為參數。
第一個參數是 Promise 執行成功時的回調,第二個參數是 Promise 執行失敗時的回調,兩個函數只會有一個被調用。
const p = new Promise(function(resolve,reject){ resolve('success'); }); p.then(function(value){ console.log(value); }); console.log('first'); // first // success
const p = new Promise(function(resolve,reject){ resolve(1); }).then(function(value){ // 第一個then // 1 console.log(value); return value * 2; }).then(function(value){ // 第二個then // 2 console.log(value); }).then(function(value){ // 第三個then // undefined console.log(value); return Promise.resolve('resolve'); }).then(function(value){ // 第四個then // resolve console.log(value); return Promise.reject('reject'); }).then(function(value){ // 第五個then //reject:reject console.log('resolve:' + value); }, function(err) { console.log('reject:' + err); });
then方法將返回一個resolved或是rejected狀態的Promise對象用於鏈式調用。
熱Promise
在JavaScript中,所有代碼都是單線程的,也就是同步執行的,promise就是為了提供一個解決方案的異步編程。
promise的特點:只有異步操作可以決定當前處於的狀態,並且任何其他操作無法改變這個狀態;一旦狀態改變,就不會在變。
狀態改變的過程:從pending變為fulfilled和從pending變為rejected,狀態改變後,就不會在改變了,這就叫已定型resolved
用法:
Promise對象是由關鍵字new及其構造函數來創建的。
const promise = new Promise((resolve, reject) => { // do something here ... if (success) { resolve(value); // fulfilled } else { reject(error); // rejected } });
函數接收兩個函數作為參數,分別是resolve和reject,當異步操作執行成功後,會將異步操作的結果作為參數傳入resolve函數並執行,此時的狀態由Promise狀態從pending變為fulfilled;而失敗會將異步操作的錯誤作為參數傳入reject函數並執行,此時Promise對象狀態從pending變為rejected。
通過then方法,將指定resolved狀態和rejected狀態的回調函數。
promise.then(function(value) { // success }, function(error) { // failure });
Promise.all(iterable),iterable必須是一個可以迭代的對象,如Array
返回值為一個新的Promise實例。
var p1 = new Promise((resolve, reject) => { setTimeout(resolve, 1000, 'one'); }); var p2 = new Promise((resolve, reject) => { setTimeout(resolve, 2000, 'two'); }); var p3 = new Promise((resolve, reject) => { setTimeout(resolve, 3000, 'three'); }); var p4 = new Promise((resolve, reject) => { reject('p4 reject!'); }); var p5 = new Promise((resolve, reject) => { reject('p5 reject!'); }); Promise.all([p1, p2, p3, p4, p5]).then(values => { console.log(values); }, reason => { console.log(reason) }); // p4 reject!
Promise.race(iterable),同理,返回值為一個新的Promise實例。
返回的新實例狀態,會是最先改變狀態的那個實例,如果不是Promise實例,先用Promise.resolve方法,如果傳入的迭代為空,則返回的Promise永久等待。
一個Promise實例 原封不動的返回該實例;
var original = Promise.resolve('第二行'); var da = Promise.resolve(original); da.then(function(value) { console.log('value: ' + value); }); console.log('original === da ? ' + (original === da)); // "original === da ? true" // "value: 第二行"
跟隨這個thenable對象的,採用它的最終狀態;
let thenable = { then: function(resolve, reject) { resolve(41); } } let p = Promise.resolve(thenable); p.then(function(value) { console.log(value); }) // 41
直接將傳入參數當最終結果,並返回一個新的Promise;
let p = Promsie.resolve(12); p.then(function(number) { console.log(number); }) // 12
直接返回一個resolved狀態的Promise對象
let p = Promsie.resovle(); p.then(function() { // do something })
Promise.prototype.then()
p.then(onResolve, onReject); p.then(function(value) { }, function(reason) { });
Promise.prototype. catch()
p.catch(onReject) p.catch(function(reason) { });
// bad promise .then(function(data) { // success }, function(err) { // error }); // good promise .then(function(data) { // success }) .catch(function(err) { // error });
Promise.prototype. finally()
p.finally(onFinally); p.finally(function() { })
該回調函數的不接受任何參數
promise是一個對象,代表一個異步操作,有三種狀態,進行中,成功,失敗。只有異步操作的結果的可以決定當前是哪種狀態,promise一旦新建執行,就沒有辦法中途停止。
Promise.all方法用於將多個Promise實例,包裝成一個新的Promise實例。只有當作為參數所有的promise函數運行完畢,才會執行.then回調。

file

file

file
//以往回調方式 函數1(function(){ //代碼執行...(ajax1) 函數2(function(){ //代碼執行...(ajax2) 函數3(function(data3){ //代碼執行...(ajax3) }); ... }); }); //Promise回調方式:鏈式調用,可構建多個回調函數。 promise().then().then()...catch()
//創建Promise實例 let promise = new Promise( (resolve, reject) => { //執行相應代碼 根據情況調用resolve或reject ... }) //promise的then方法中執行回調 promise.then(function(){ //第一個參數是返回resolve時 },function(){ //第二個是回調返回reject時 } }
定時器調用
const promise = new Promise(function(resolve, reject){ setTimeout(resolve,1000); }) promise.then(function(){ console.log('resolve:成功回調函數') },function(){ console.log('reject:失敗回調函數') })

file
傳遞參數:
const promise = new Promise((resolve, reject) => { setTimeout(reject,1000,'我是value值'); }) promise.then((value) => { console.log('resolve:' + value) }).catch((value) => { console.log('reject:'+ value) }) //第一種,單個傳值是無效的 const promise = new Promise((resolve, reject) => { setTimeout(resolve,1000,'參數1','參數2'); }) promise.then((value1,value2) => { console.log('value1:' + value1) //value1:參數1 console.log('value2:' + value2) //value2:undefined }).catch((value) => { console.log(value) }) //第二種:數組傳值 const promise = new Promise((resolve, reject) => { setTimeout(resolve,1000,['參數1','參數2']); }) promise.then((value1) => { console.log('value1:' + value1) //value1:參數1,參數2 }).catch((value) => { console.log(value) })
Promise.prototype.then方法:鏈式操作
getJSON("/posts.json").then(function(json) { return json.post; }).then(function(post) { // proceed });
Promise.prototype.catch方法:捕捉錯誤
getJSON("/posts.json").then(function(posts) { }).catch(function(error) { console.log('發生錯誤!', error); });
Promise.all方法,Promise.race方法
var p = Promise.all([p1,p2,p3]); var p = Promise.race([p1,p2,p3]);


