最近越來越喜歡與大家進行資源分享了,並且及時的同步到自己的園子內,為什么呢?
一、小插曲(氣氛搞起)
在上個月末,由於領導的高度重視(haha,這個高度是有多高呢,185就好了),走進了公司骨干員工的隊列,並參與為骨干員工准備的“高效能人士的七項修煉”課程培訓。
那接下來我是不是該簡明扼要的說一下七項修煉有哪些,很受用哦。
七項修煉之一:積極主動 ==> 積極心態去處理事情、不怕事。
七項修煉之二:明確方向 ==> 要做到以終為始,將終點作為起點,幫助你實現生活與工作中的重要目標。
七項修煉之三:要事優先 ==> 主要針對時間安排、輕重事的划分,可以參考以下象限圖。
第一象限:重要且緊急的事情放到首位去解決,這個象限的事情不做那你就真傻咯,等着被炒吧 2333333。
第二象限:有些事情是重要但不緊急的,一定不能拖,拖久了這件事情會變成緊急並且重要,這樣下來你就永遠在處理重要且緊急的事情,所以說處理完第一象限趕緊處理第二象限的。
第三象限:有些事情是特別緊急但不重要的,總會去占用你的時間去處理,並且對你沒有什么意義,像這類沒有意義、對你影響不大的事情就可以直接丟掉了,或者交給你的下屬去處理。
第四象限:該象限就可以想而知了,不重要、不緊急,所以堅決不做。
七項修煉之四:雙贏思維 ==> 是指 “我們” 而非 “我”,一起尋求互惠思考與心意,目的是獲得更豐富的機會,財富及資源,而非患不足的敵對式競爭,還必須做到高勇氣高體諒哦。
七項修煉之五:知彼解己 ==> 學會如何去溝通,80%的溝通障礙來自於聽(聽==>以耳為王,十目一心),一定要帶着理解對方的目的去傾聽。
七項修煉之六:協作增效 ==> 三個臭皮匠頂個諸葛亮、瞎子背瘸子的故事大家都聽過吧,那如何實現1+1>2呢?(仔細體會吧)。
七項修煉之七:不斷更新 ==> 有了前六項了,當然要不斷地更新、完善提升自己了。
最后借鑒Samuel Smiles的一句話送給大家:“思想引發行為,行為漸成習慣,習慣塑造品格,品格決定命運”。
以上講了一堆自己培訓之后的小感悟,小收獲吧。
二、步入正文,什么是Promise,能夠解決什么問題?
我們知道,JavaScript的執行環境是單線程,所謂的單線程是指一個任務執行完成之后才可以執行下一個任務,如若某一個任務執行出錯,會導致下一個任務無法執行。但是在JavaScript中也提供了一些異步模式。
以下是兩段特別經典的異步模式:
function setTime(callback){ setTimeout(()=>{ callback(); console.log("我被最后輸出,慘那。"); },0); console.log("異步執行代碼。"); }; setTime(()=>{ console.log("多會可以執行我呢?"); }); //異步執行代碼。 //多會可以執行我呢? //我被最后輸出,慘那。
function getData(url,callback, errorCallback) { $.ajax({ url: url, success: function (res) { //成功函數
callback(res); }, fail: function (res) { //失敗函數
errorCallback(res); } }); } getData("../url/url",(res)=>{ console.log("請求成功回調。"); },(res)=>{ console.log("請求失敗回調。"); })
上面的兩段代碼看起來貌似沒有什么問題。
是的,並不存在什么問題。但是在需求的代碼化中,這樣的異步模式往往會出現嵌套的形式,那它的展現形式如下。
setTime(()=>{ setTime(()=>{ setTime(()=>{ setTime(()=>{ }); }); }); console.log("多會可以執行我呢?"); });
getData("../url/url",(res)=>{ getData("../url/url",(res)=>{ console.log("請求成功回調。"); },(res)=>{ console.log("請求失敗回調。"); }) console.log("請求成功回調。"); },(res)=>{ console.log("請求失敗回調。"); })
類似這樣的層層嵌套,它的執行順序就不好把握了,維護性以及可讀性也就可想而知了。重要的是回調函數剝奪了我們使用 return 和 throw 這類關鍵字的能力。
1.說了這么多,我們的Promise該閃亮登場了✨,Promise 很好的解決以上全部問題,是異步編程的一種解決方案,它由社區最早提出和實現,ES6將其寫進了語言標准,統一了用法,原生提供了 Promise 對象。
所謂Promise,簡單說就是一個容器,里面保存着某個未來才會結束的事件(通常是一個異步操作)的結果。
Promise對象有兩個特點:
1】Promise對象的狀態不受外界影響。Promise對象存在三種狀態:pending(進行中)、fulfilled(已成功)和reject(已失敗),只有異步操作的結果,可以決定是哪一種狀態,任何其他操作都無法發變這個狀態。
這也就是Promise這個名字的由來。他的英文意思就是“承諾”,表示通過其他手段無法改變。
2】一旦狀態改變,就不會再變,任何時候都可以得到這個結果。
Promise對象的狀態改變,只有兩種可能:從pending變為fulfilled和從pending變為rejected。只要這兩種情況發生,狀態就凝固了,不會再變了,會一直保持這個結果,這時就稱為 resolved(已定型)。如果改變已經發生了,你再對Promise對象添加回調函數,也會立即得到這個結果。這與事件(Event)完全不同,事件的特點是,如果你錯過了它,再去監聽,是得不到結果的。
Promise也有一些缺點。首先,無法取消Promise,一旦新建它就會立即執行,無法中途取消。其次,如果不設置回調函數,Promise內部拋出的錯誤,不會反應到外部。第三,當處於pending狀態時,無法得知目前進展到哪一個階段(剛剛開始還是即將完成)。
2.基本用法:
ES6規定,Promise 對象是一個構造函數,用來生成Promise 實例。
下面的代碼就是一個Promise 實例:
var promise=new Promise(function(resolve,reject){ //code
if(/*異步操作*/){ resolve(); }else{ reject(); } })
Promise構造函數接受一個函數作為參數,該函數有兩個參數分別是resolve和reject,它們也是函數。
resolve函數的作用是,將Promise 對象的狀態從“未完成”(pending)==>“成功”(resolved),在異步操作成功時調用,並將異步操作結果,作為參數傳遞出去。
reject函數的作用是,將Promise 對象的狀態從“未完成”(pending)==>“失敗”(rejected),再異步操作失敗時調用,並將操作報錯的錯誤,作為參數傳遞出去。
Promise
實例生成以后,可以用then
方法分別指定resolved
狀態和rejected
狀態的回調函數。
promise.then(function(value) { // success
}, function(error) { // failure
}); 等同於
promise.then( value=>{ // success
}, error=> { // failure
} );
then
方法可以接受兩個回調函數作為參數。第一個回調函數是Promise
對象的狀態變為resolved
時調用,第二個回調函數是Promise
對象的狀態變為rejected
時調用。其中,第二個函數是可選的,不一定要提供。這兩個函數都接受Promise
對象傳出的值作為參數。
下面是一個Promise
對象的簡單例子。
function timeout(ms) { return new Promise((resolve, reject) => { setTimeout(resolve, ms, 'done'); }); } timeout(100).then((value) => { console.log(value); }); //done
上面代碼中,timeout
方法返回一個Promise
實例,表示一段時間以后才會發生的結果。過了指定的時間(ms
參數)以后,Promise
實例的狀態變為resolved
,就會觸發then
方法綁定的回調函數。
Promise 新建后就會立即執行。
var promise = new Promise(function(resolve, reject) { console.log('Promise'); resolve(); }); promise.then(function() { console.log('resolved.'); }); console.log('Hi!'); // Promise // Hi! // resolved
上面代碼中,Promise 新建后立即執行,所以首先輸出的是Promise
。然后,then
方法指定的回調函數,將在當前腳本所有同步任務執行完才會執行,所以resolved
最后輸出。
3.then用法:前面說過,then方法的第一個參數是resolved狀態的回調函數。第二個參數(可選)是rejected狀態的回調函數。
var promise=new Promies(function(resolve,reject){ if(/*異步執行成功*/){ resolve(); }else{ reject(); } }) promise().then( res=>{ console.log(res); },error=>{ console.log(error); } )
因此可以采用鏈式寫法,即then后在調用另一個then方法。
promise() .then((res)=>{ //code
return res.a; }) .then((res)=>{ //code
return res.b; }) .then((res)=>{ //code
return res.c; })
上面的代碼使用then
方法,依次指定了兩個回調函數。第一個回調函數完成以后,會將返回結果作為參數,傳入第二個回調函數。
4.catch用法:是用於指定發生錯時的回調函數,即為Promise從未完成到失敗的回調函數。
下面是這個例子。
var promise = new Promise(function(resolve, reject) { reject(); }); promise.catch(function(error) { console.log(error); }); // Error: test
另外,then方法指定的回調函數,在運行中拋出錯誤時,也會被catch方法捕獲。
var promise = new Promise(function(resolve, reject) { throw new Error('test'); }); promise.catch(function(error) { console.log(error); }); // Error: test
上面代碼中,promise
拋出一個錯誤,就被catch
方法指定的回調函數捕獲。
如果 Promise 狀態已經變成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 在resolve
語句后面,再拋出錯誤,不會被捕獲,等於沒有拋出。因為 Promise 的狀態一旦改變,就永久保持該狀態,不會再變了。
Promise 對象的錯誤具有“冒泡”性質,會一直向后傳遞,直到被捕獲為止。也就是說,錯誤總是會被下一個catch
語句捕獲。
getJSON('/post/1.json').then(function(post) { return getJSON(post.commentURL); }).then(function(comments) { // some code
}).catch(function(error) { // 處理前面三個Promise產生的錯誤
});
上面代碼中,一共有三個 Promise 對象:一個由getJSON
產生,兩個由then
產生。它們之中任何一個拋出的錯誤,都會被最后一個catch
捕獲。
一般來說,不要在then
方法里面定義 Reject 狀態的回調函數(即then
的第二個參數),總是使用catch
方法。
// bad
promise .then(function(data) { // success
}, function(err) { // error
}); // good
promise .then(function(data) { //cb
// success
}) .catch(function(err) { // error
});
上面代碼中,第二種寫法要好於第一種寫法,理由是第二種寫法可以捕獲前面then
方法執行中的錯誤,也更接近同步的寫法(try/catch
)。因此,建議總是使用catch
方法,而不使用then
方法的第二個參數。
5. all用法:提供了並行執行異步操作的能力,並且在所有異步操作完成后才執行回調函數 ==> 【誰跑的慢,以誰為准執行回調】。
看下面例子:
function runAsync1(){ var p = new Promise(function(resolve, reject){ //做一些異步操作
setTimeout(function(){ console.log('異步任務1執行完成'); resolve('隨便什么數據1'); }, 1000); }); return p; } function runAsync2(){ var p = new Promise(function(resolve, reject){ //做一些異步操作
setTimeout(function(){ console.log('異步任務2執行完成'); resolve('隨便什么數據2'); }, 2000); }); return p; } function runAsync3(){ var p = new Promise(function(resolve, reject){ //做一些異步操作
setTimeout(function(){ console.log('異步任務3執行完成'); resolve('隨便什么數據3'); }, 2000); }); return p; } Promise.all([runAsync1(),runAsync2(),runAsync3()]).then((res)=>{ }) //異步任務1執行完成 //異步任務2執行完成 //異步任務3執行完成 //["隨便什么數據1","隨便什么數據2","隨便什么數據3"]
用Promise.all來執行,all接收一個數組作為參數,里面的值最終會返回Promise對象。這樣三個異步操作的並行執行,等到它們都執行完后才會進入then里面。all會把三個異步操作的結果放到一個數組中傳給then。也就是說有了all,你就可以並行執行多個異步操作,並且在一個回調函數中處理所有異步操作返回的數據。是不是炫酷吊炸天?有一個場景很適用這個,在素材比較多的應用,預先加載需要用到的各種資源,所有加載完后,我們在進行頁面的初始化。
6.race用法:同樣是並行異步操作,與all方法不同的是,率先完成異步操作的就立馬執行回調函數 ==> 【誰跑的快,以誰為准執行回調】。
function runAsync1(){ var p = new Promise(function(resolve, reject){ //做一些異步操作
setTimeout(function(){ console.log('異步任務1執行完成'); resolve('隨便什么數據1'); }, 1000); }); return p; } function runAsync2(){ var p = new Promise(function(resolve, reject){ //做一些異步操作
setTimeout(function(){ console.log('異步任務2執行完成'); resolve('隨便什么數據2'); }, 2000); }); return p; } function runAsync3(){ var p = new Promise(function(resolve, reject){ //做一些異步操作
setTimeout(function(){ console.log('異步任務3執行完成'); resolve('隨便什么數據3'); }, 2000); }); return p; } Promise.all([runAsync1(),runAsync2(),runAsync3()]).then((res)=>{ }) //異步任務1執行完成 //隨便什么數據1 //異步任務2執行完成 //異步任務3執行完成
在then里面回調開始執行時,runAsync2()和runAsync3()並未停止,仍舊再執行,於是1秒后,輸給了他們的結束標志。
總結:ES6 Promise 的內容就這些么? 是的,能用到的基本就是這些。
關於Promise 就說這些了,有疑問隨時留言哦,有不對之處,請指正。謝謝。
引用阮大神:http://es6.ruanyifeng.com/#docs/promise