(名字含義:promise爲承諾,表示其餘手段沒法改變)javascript
1.解決回調地獄php
2.更好地進行錯誤捕獲css
有時咱們要進行一些相互間有依賴關係的異步操做,好比有多個請求,後一個的請求須要上一次請求的返回結果。過去常規作法只能 callback 層層嵌套,但嵌套層數過多的話就會有 callback hell 問題。好比下面代碼,可讀性和維護性都不好的。html
firstAsync(function(data){ //處理獲得的 data 數據 //.... secondAsync(function(data2){ //處理獲得的 data2 數據 //.... thirdAsync(function(data3){ //處理獲得的 data3 數據 //.... }); }); });
promise例子1前端
//建立一個Promise實例,獲取數據。並把數據傳遞給處理函數resolve和reject。須要注意的是Promise在聲明的時候就執行了。 var getUserInfo=new Promise(function(resolve,reject){ $.ajax({ type:"get", url:"index.aspx", success:function(){ if(res.code=="ok"){ resolve(res.msg)//在異步操做成功時調用 }else{ reject(res.msg);//在異步操做失敗時調用 } } }); }) //另外一個ajax Promise對象, var getDataList=new Promise(function(resolve,reject){ $.ajax({ type:"get", url:"index.aspx", success:function(res){ if(res.code=="ok"){ resolve(res.msg)//在異步操做成功時調用 }else{ reject(res.msg);//在異步操做失敗時調用 } } }); }) //Promise的方法then,catch方法 getUserInfo.then(function(ResultJson){ //經過拿到的數據渲染頁面 }).catch(function(ErrMsg){ //獲取數據失敗時的處理邏輯 }) //Promise的all方法,等數組中的全部promise對象都完成執行 Promise.all([getUserInfo,getDataList]).then(function([ResultJson1,ResultJson2]){ //這裏寫等這兩個ajax都成功返回數據才執行的業務邏輯 })
注意:成功的結果須要用resolve包裹,失敗的結果須要用reject包裹 vue
promise例子2java
ajax請求node
var getData=new Promise(function(resolve,reject){ $.post("http://apptest.hcbkeji.com/php/option/activity/chevron_report_activity.php", {flag: 'click',act:'臨沂頁面',page:'臨沂上報活動'}, function (res) { resolve(res) } ); }) getData.then(res=>{ console.log(res) //{"type":"ok"} })
promise例子3ios
var test=new Promise((resolve,reject)=>{ setTimeout(function(){ resolve('hello world') },2000) }) test.then(res=>{ console.log(res) }) console.log('雖然在後面,可是我先執行') //打印結果 // 雖然在後面,可是我先執行 // hello world
promise例子4git
function mytest(){ return new Promise((resolve,reject)=>{ $.post("http://apptest.hcbkeji.com/php/option/activity/chevron_report_activity.php", {flag: 'click',act:'臨沂頁面',page:'臨沂上報活動'}, function (res) { var res=JSON.parse(res) resolve(res) } ); }) } mytest().then(res=>{ console.log(res) }) console.log('雖然在後面,可是我先執行')
做爲一個關鍵字放到函數前面,用於表示函數是一個異步函數,由於async就是異步的意思, 異步函數也就意味着該函數的執行不會阻塞後面代碼的執行
調用方法:async 函數也是函數,平時咱們怎麼使用函數就怎麼使用它,直接加方法名括號調用。
async function test(){ return 'hello world' } test().then(res=>{ console.log(res) }) console.log('雖然在後面,可是我先執行') //打印結果 //雖然在後面,可是我先執行 //hello world
首先打印 ‘雖然在後面,可是我先執行’ ,後執行 打印 ‘hello world’
async的做用:輸出的是一個 Promise 對象
async function testAsync() { return "hello async"; } let result = testAsync(); console.log(result)
打印結果Promise {<resolved>: "hello async"} 從結果中能夠看到async函數返回的是一個promise對象,若是在函數中 return 一個直接量,async 會把這個直接量經過 Promise.resolve()
封裝成 Promise 對象。
async function testAsync1() { console.log("hello async"); } let result1 = testAsync1(); console.log(result1);
結果返回Promise.resolve(undefined) 由於使用async 可是函數沒有return一個直接量
async 函數(包含函數語句、函數表達式、Lambda表達式)會返回一個 Promise 對象,若是在函數中 return
一個直接量,async 會把這個直接量經過 Promise.resolve()
封裝成 Promise 對象。
async 函數返回的是一個 Promise 對象,因此在最外層不能用 await 獲取其返回值的狀況下,咱們固然應該用原來的方式:then()
鏈來處理這個 Promise 對象。
Promise 的特色是無等待,因此在沒有 await
的狀況下執行 async 函數,它會當即執行,返回一個 Promise 對象,而且,毫不會阻塞後面的語句。這和普通返回 Promise 對象的函數並沒有二致。
Promise 有一個resolved,這是async 函數內部的實現原理。若是async 函數中有返回一個值 ,當調用該函數時,內部會調用Promise.solve() 方法把它轉化成一個promise 對象做爲返回,但若是timeout 函數內部拋出錯誤呢? 那麼就會調用Promise.reject() 返回一個promise 對象, 這時修改一下timeout 函數
async function timeout(flag) { if (flag) { return 'hello world' } else { throw 'my god, failure' } } console.log(timeout(true)) // 調用Promise.resolve() 返回promise 對象。 console.log(timeout(false)); // 調用Promise.reject() 返回promise 對象。
控制檯以下:
若是函數內部拋出錯誤, promise 對象有一個catch 方法進行捕獲。
timeout(false).catch(err => { console.log(err) })
由於 async 函數返回一個 Promise 對象,因此 await 能夠用於等待一個 async 函數的返回值——這也能夠說是 await 在等 async 函數,但要清楚,它等的實際是一個返回值。注意到 await 不只僅用於等 Promise 對象,它能夠等任意表達式的結果,因此,await 後面實際是能夠接普通函數調用或者promise對象
注意:await 命令只能用在 async 函數之中,若是用在普通函數,就會報錯。
function getSomething() { return "something"; } async function testAsync() { return Promise.resolve("hello async"); } async function test() { const v1 = await getSomething(); //await後接普通函數調用 const v2 = await testAsync(); //await後接async promise對象 console.log(v1, v2); } test(); //打印結果something hello async
await
是個運算符,用於組成表達式,await 表達式的運算結果取決於它等的東西。
若是它等到的不是一個 Promise 對象,那 await 表達式的運算結果就是它等到的東西。
若是它等到的是一個 Promise 對象,await 就忙起來了,它會阻塞後面的代碼,等着 Promise 對象 resolve,而後獲得 resolve 的值,做爲 await 表達式的運算結果。
await 必須用在 async 函數中的緣由。async 函數調用不會形成阻塞,它內部全部的阻塞都被封裝在一個 Promise 對象中異步執行。
綜合:上面已經說明了 async 會將其後的函數(函數表達式或 Lambda)的返回值封裝成一個 Promise 對象,而 await 會等待這個 Promise 完成,並將其 resolve 的結果返回出來。
使用promise和async await比較
使用promise
function takeLongTime(n){ return new Promise(resolve=>{ setTimeout(()=>resolve(n+200),n) }) } function step1(n){ console.log(`step1 with ${n}`) return takeLongTime(n); } function step2(n){ console.log(`step2 with ${n}`) return takeLongTime(n); } function step3(n){ console.log(`step3 with ${n}`) return takeLongTime(n); } function run(){ console.time('run') const time1=300; step1(time1) .then(time2=>step2(time2)) .then(time3=>step3(time3)) .then(result=>{ console.log(`resutlt is ${result}`) console.timeEnd('run') }) } run()
打印結果
step1 with 300 step2 with 500 step3 with 700 run: 1504.652099609375ms
使用async await
function takeLongTime(n){ return new Promise(resolve=>{ setTimeout(()=>resolve(n+200),n) }) } function step1(n){ console.log(`step1 with ${n}`) return takeLongTime(n); } function step2(n){ console.log(`step2 with ${n}`) return takeLongTime(n); } function step3(n){ console.log(`step3 with ${n}`) return takeLongTime(n); } async function run(){ console.time('run') const time1=300 const time2=await step1(time1) const time3=await step1(time2) const result=await step1(time3) console.log(`result is ${result}`) console.timeEnd('run') } run()
結果和以前的 Promise 實現是同樣的,可是這個代碼看起來是否是清晰得多,幾乎跟同步代碼同樣
await 命令後面的 Promise 對象,運行結果多是 rejected,因此最好把 await 命令放在 try...catch 代碼塊中
async function myFunction() { try { await somethingThatReturnsAPromise(); } catch (err) { console.log(err); } } // 另外一種寫法 async function myFunction() { await somethingThatReturnsAPromise().catch(function (err){ console.log(err); }); }
async function dbFuc(db) { let docs = [{}, {}, {}]; // 報錯 docs.forEach(function (doc) { await db.post(doc); }); }
await 關鍵字,await是等待的意思,那麼它等待什麼呢,它後面跟着什麼呢?其實它後面能夠聽任何表達式,不過咱們更多的是放一個返回promise 對象的表達式。注意await 關鍵字只能放到async 函數裏面
如今寫一個函數,讓它返回promise 對象,該函數的做用是2s 以後讓數值乘以2
// 2s 以後返回雙倍的值 function doubleAfter2seconds(num) { return new Promise((resolve, reject) => { setTimeout(() => { resolve(2 * num) }, 2000); } ) }
如今再寫一個async 函數,從而可使用await 關鍵字, await 後面放置的就是返回promise對象的一個表達式,因此它後面能夠寫上 doubleAfter2seconds 函數的調用
async function testResult() { let result = await doubleAfter2seconds(30); console.log(result); }
如今調用testResult 函數
testResult();
打開控制檯,2s 以後,輸出了60.
如今咱們看看代碼的執行過程,調用testResult 函數,它裏面遇到了await, await 表示等一下,代碼就暫停到這裏,再也不向下執行了,它等什麼呢?等後面的promise對象執行完畢,而後拿到promise resolve 的值並進行返回,返回值拿到以後,它繼續向下執行。具體到 咱們的代碼, 遇到await 以後,代碼就暫停執行了, 等待doubleAfter2seconds(30) 執行完畢,doubleAfter2seconds(30) 返回的promise 開始執行,2秒 以後,promise resolve 了, 並返回了值爲60, 這時await 纔拿到返回值60, 而後賦值給result, 暫停結束,代碼纔開始繼續執行,執行 console.log語句。
就這一個函數,咱們可能看不出async/await 的做用,若是咱們要計算3個數的值,而後把獲得的值進行輸出呢?
async function testResult() { let first = await doubleAfter2seconds(30); let second = await doubleAfter2seconds(50); let third = await doubleAfter2seconds(30); console.log(first + second + third); }
6秒後,控制檯輸出220, 咱們能夠看到,寫異步代碼就像寫同步代碼同樣了,再也沒有回調地域了。
設計使用場景,一個手機充值活動,實現選擇用戶的省市,點擊充值按鈕,彈出相應省份的充值列表,2個請求。
請求1 獲取所在省市: 根據手機號獲得省和市 方法命名爲getLocation 接受一個參數phoneNum 返回結果 province 和city
請求2 獲取可充值面值列表:根據省和市獲得充值面值列表 方法命名爲getFaceList 接受兩個參數province 和city 返回充值列表
咱們首先要根據手機號獲得省和市,因此寫一個方法來發送請求獲取省和市,方法命名爲getLocation, 接受一個參數phoneNum ,當獲取到城市位置之後,咱們再發送請求獲取充值面值,因此還要再寫一個方法getFaceList, 它接受兩個參數, province 和city,
methods: { //獲取到城市信息 getLocation(phoneNum) { return axios.post('phoneLocation', {phoneNum:phoneNum}) }, // 獲取面值 getFaceList(province, city) { return axios.post('/faceList', {province:province,city:city}) }, // 點擊肯定按鈕時,顯示面值列表 getFaceResult () { this.getLocation(this.phoneNum) .then(res => { if (res.code=='ok') { let province = res.data.obj.province; let city = res.data.obj.city; this.getFaceList(province, city) .then(res => { if(res.code=='ok') { //最終獲取到面值列表 this.faceList = res.data.obj } }) } }) .catch(err => { console.log(err) }) } }
如今點擊肯定按鈕,能夠看到頁面中輸出了 從後臺返回的面值列表。這時你看到了then 的鏈式寫法,有一點回調地域的感受。如今咱們在有async/ await 來改造一下。
首先把 getFaceResult 轉化成一個async 函數,就是在其前面加async, 由於它的調用方法和普通函數的調用方法是一致,因此沒有什麼問題。而後就把 getLocation 和
methods: { //獲取到城市信息 getLocation(phoneNum) { return axios.post('phoneLocation', {phoneNum:phoneNum}) }, // 獲取面值 getFaceList(province, city) { return axios.post('/faceList', {province:province,city:city}) }, // 點擊肯定按鈕時,顯示面值列表 async getFaceResult () { let location = await this.getLocation(this.phoneNum); if (location.code=='ok') { let province = location.data.obj.province; let city = location.data.obj.city; let result = await this.getFaceList(province, city); if (result.code=='ok') { this.faceList = result.data.obj; } } } }
如今代碼的書寫方式,就像寫同步代碼同樣,沒有回調的感受,很是舒服。
如今就還差一點須要說明,那就是怎麼處理異常,若是請求發生異常,怎麼處理? 它用的是try/catch 來捕獲異常,把await 放到 try 中進行執行,若有異常,就使用catch 進行處理。
methods: { //獲取到城市信息 getLocation(phoneNum) { return axios.post('phoneLocation', {phoneNum:phoneNum}) }, // 獲取面值 getFaceList(province, city) { return axios.post('/faceList', {province:province,city:city}) }, // 點擊肯定按鈕時,顯示面值列表 async getFaceResult () { try { let location = await this.getLocation(this.phoneNum); if (location.code=='ok') { let province = location.data.obj.province; let city = location.data.obj.city; let result = await this.getFaceList(province, city); if (result.code=='ok') { this.faceList = result.data.obj; } } } catch(err) { console.log(err); } } }
參考:https://www.cnblogs.com/SamWeb/p/8417940.html
異步編程模式在前端開發過程當中,顯得愈來愈重要。從最開始的XHR到封裝後的Ajax都在試圖解決異步編程過程當中的問題。隨着ES6新標準的到來,處理異步數據流又有了新的方案。咱們都知道,在傳統的ajax請求中,當異步請求之間的數據存在依賴關係的時候,就可能產生很難看的多層回調,俗稱'回調地獄'(callback hell),這卻讓人望而生畏,Promise的出現讓咱們告別回調函數,寫出更優雅的異步代碼。在實踐過程當中,卻發現Promise並不完美,Async/Await是近年來JavaScript添加的最革命性的的特性之一,Async/Await提供了一種使得異步代碼看起來像同步代碼的替代方法。接下來咱們介紹這兩種處理異步編程的方案。
Promise 是一種對異步操做的封裝,能夠經過獨立的接口添加在異步操做執行成功、失敗時執行的方法。主流的規範是 Promises/A+。
Promise中有幾個狀態:
pending: 初始狀態, 非 fulfilled 或 rejected;
fulfilled: 成功的操做,爲表述方便,fulfilled 使用 resolved 代替;
rejected: 失敗的操做。
pending能夠轉化爲fulfilled或rejected而且只能轉化一次,也就是說若是pending轉化到fulfilled狀態,那麼就不能再轉化到rejected。而且fulfilled和rejected狀態只能由pending轉化而來,二者之間不能互相轉換。
Promise實例必須實現then這個方法
then()必須能夠接收兩個函數做爲參數
then()返回的必須是一個Promise實例
<script src="https://cdn.bootcss.com/bluebird/3.5.1/bluebird.min.js"></script>//若是低版本瀏覽器不支持Promise,經過cdn這種方式 <script type="text/javascript"> function loadImg(src) { var promise = new Promise(function (resolve, reject) { var img = document.createElement('img') img.onload = function () { resolve(img) } img.onerror = function () { reject('圖片加載失敗') } img.src = src }) return promise } var src = 'https://www.imooc.com/static/img/index/logo_new.png' var result = loadImg(src) result.then(function (img) { console.log(1, img.width) return img }, function () { console.log('error 1') }).then(function (img) { console.log(2, img.height) }) </script>
Promise還能夠作更多的事情,好比,有若干個異步任務,須要先作任務1,若是成功後再作任務2,任何任務失敗則再也不繼續並執行錯誤處理函數。要串行執行這樣的異步任務,不用Promise須要寫一層一層的嵌套代碼。
有了Promise,咱們只須要簡單地寫job1.then(job2).then(job3).catch(handleError);
其中job一、job2和job3都是Promise對象。
好比咱們想實現第一個圖片加載完成後,再加載第二個圖片,若是其中有一個執行失敗,就執行錯誤函數:
var src1 = 'https://www.imooc.com/static/img/index/logo_new.png' var result1 = loadImg(src1) //result1是Promise對象 var src2 = 'https://img1.mukewang.com/545862fe00017c2602200220-100-100.jpg' var result2 = loadImg(src2) //result2是Promise對象 result1.then(function (img1) { console.log('第一個圖片加載完成', img1.width) return result2 // 鏈式操做 }).then(function (img2) { console.log('第二個圖片加載完成', img2.width) }).catch(function (ex) { console.log(ex) })
這裏需注意的是:then 方法能夠被同一個 promise 調用屢次,then 方法必須返回一個 promise 對象。上例中result1.then若是沒有明文返回Promise實例,就默認爲自己Promise實例即result1,result1.then返回了result2實例,後面再執行.then實際上執行的是result2.then
除了串行執行若干異步任務外,Promise還能夠並行執行異步任務。
試想一個頁面聊天系統,咱們須要從兩個不一樣的URL分別得到用戶的我的信息和好友列表,這兩個任務是能夠並行執行的,用Promise.all()實現以下:
var p1 = new Promise(function (resolve, reject) { setTimeout(resolve, 500, 'P1'); }); var p2 = new Promise(function (resolve, reject) { setTimeout(resolve, 600, 'P2'); }); // 同時執行p1和p2,並在它們都完成後執行then: Promise.all([p1, p2]).then(function (results) { console.log(results); // 得到一個Array: ['P1', 'P2'] });
有些時候,多個異步任務是爲了容錯。好比,同時向兩個URL讀取用戶的我的信息,只須要得到先返回的結果便可。這種狀況下,用Promise.race()實現:
var p1 = new Promise(function (resolve, reject) { setTimeout(resolve, 500, 'P1'); }); var p2 = new Promise(function (resolve, reject) { setTimeout(resolve, 600, 'P2'); }); Promise.race([p1, p2]).then(function (result) { console.log(result); // 'P1' });
因爲p1執行較快,Promise的then()將得到結果'P1'。p2仍在繼續執行,但執行結果將被丟棄。
總結:Promise.all接受一個promise對象的數組,待所有完成以後,統一執行success;
Promise.race接受一個包含多個promise對象的數組,只要有一個完成,就執行success
接下來咱們對上面的例子作下修改,加深對這二者的理解:
var src1 = 'https://www.imooc.com/static/img/index/logo_new.png' var result1 = loadImg(src1) var src2 = 'https://img1.mukewang.com/545862fe00017c2602200220-100-100.jpg' var result2 = loadImg(src2) Promise.all([result1, result2]).then(function (datas) { console.log('all', datas[0])//<img src="https://www.imooc.com/static/img/index/logo_new.png"> console.log('all', datas[1])//<img src="https://img1.mukewang.com/545862fe00017c2602200220-100-100.jpg"> }) Promise.race([result1, result2]).then(function (data) { console.log('race', data)//<img src="https://img1.mukewang.com/545862fe00017c2602200220-100-100.jpg"> })
若是咱們組合使用Promise,就能夠把不少異步任務以並行和串行的方式組合起來執行
異步操做是 JavaScript 編程的麻煩事,不少人認爲async函數是異步操做的終極解決方案。
async/await是寫異步代碼的新方式,優於回調函數和Promise。
async/await是基於Promise實現的,它不能用於普通的回調函數。
async/await與Promise同樣,是非阻塞的。
async/await使得異步代碼看起來像同步代碼,再也沒有回調函數。可是改變不了JS單線程、異步的本質。
使用await,函數必須用async標識
await後面跟的是一個Promise實例
須要安裝babel-polyfill,安裝後記得引入 //npm i --save-dev babel-polyfill
function loadImg(src) { const promise = new Promise(function (resolve, reject) { const img = document.createElement('img') img.onload = function () { resolve(img) } img.onerror = function () { reject('圖片加載失敗') } img.src = src }) return promise } const src1 = 'https://www.imooc.com/static/img/index/logo_new.png' const src2 = 'https://img1.mukewang.com/545862fe00017c2602200220-100-100.jpg' const load = async function(){ const result1 = await loadImg(src1) console.log(result1) const result2 = await loadImg(src2) console.log(result2) } load()
當函數執行的時候,一旦遇到 await 就會先返回,等到觸發的異步操做完成,再接着執行函數體內後面的語句。
await 命令後面的 Promise 對象,運行結果多是 rejected,因此最好把 await 命令放在 try...catch 代碼塊中。try..catch錯誤處理也比較符合咱們日常編寫同步代碼時候處理的邏輯。
async function myFunction() { try { await somethingThatReturnsAPromise(); } catch (err) { console.log(err); } }
Async/Await較Promise有諸多好處,如下介紹其中三種優點:
使用Async/Await明顯節約了很多代碼。咱們不須要寫.then,不須要寫匿名函數處理Promise的resolve值,也不須要定義多餘的data變量,還避免了嵌套代碼。
你極可能遇到過這樣的場景,調用promise1,使用promise1返回的結果去調用promise2,而後使用二者的結果去調用promise3。你的代碼極可能是這樣的:
const makeRequest = () => { return promise1() .then(value1 => { return promise2(value1) .then(value2 => { return promise3(value1, value2) }) }) }
使用async/await的話,代碼會變得異常簡單和直觀
const makeRequest = async () => { const value1 = await promise1() const value2 = await promise2(value1) return promise3(value1, value2) }
下面示例中,須要獲取數據,而後根據返回數據決定是直接返回,仍是繼續獲取更多的數據。
const makeRequest = () => { return getJSON() .then(data => { if (data.needsAnotherRequest) { return makeAnotherRequest(data) .then(moreData => { console.log(moreData) return moreData }) } else { console.log(data) return data } }) }
代碼嵌套(6層)可讀性較差,它們傳達的意思只是須要將最終結果傳遞到最外層的Promise。使用async/await編寫能夠大大地提升可讀性:
const makeRequest = async () => { const data = await getJSON() if (data.needsAnotherRequest) { const moreData = await makeAnotherRequest(data); console.log(moreData) return moreData } else { console.log(data) return data } }