學(xué)習(xí)筆記20170601">【PMP】學(xué)習(xí)筆記20170601
868
2022-05-30
一.什么是Promise:
Promise 是在 js 中進(jìn)行異步編程的新解決方案。(以前舊的方案是單純使用回調(diào)函數(shù))
從語法來說,promise是一個構(gòu)造函數(shù)。
從功能來說,promise對象用來封裝一個異步操作,并且可以獲得成功或失敗的返回值。
JS中的常見的異步操作:定時器,AJAX中一般也是異步操作(也可以同步),回調(diào)函數(shù)可以理解為異步(不是嚴(yán)謹(jǐn)?shù)漠惒讲僮鳎取?/p>
剩下的都是同步處理
二.為啥使用Promise:
promise使用回調(diào)函數(shù)更靈活。舊的回調(diào)函數(shù)必須在啟動異步任務(wù)前指定。
promise:啟動異步任務(wù) => 返回promise對象 => 給promise對象綁定回調(diào)函數(shù)(甚至能在異步任務(wù)結(jié)束后指定多個)
promise支持鏈?zhǔn)秸{(diào)用,可以解決回調(diào)地獄問題。(回調(diào)地獄就是多層回調(diào)函數(shù)嵌套使用,就是套娃,這樣就不利于閱讀和異常處理。)
三. promise初體驗(yàn):
效果:點(diǎn)擊一個按鈕,有30%概率顯示中獎。
實(shí)現(xiàn): 點(diǎn)擊按鈕后得到一個1到100間的隨機(jī)數(shù),小于等于30輸出中獎,否則輸出沒中。期間用定時器模擬異步操作,在定時器里執(zhí)行判斷。
(1)基礎(chǔ)的寫法:
(2)promise寫法,在promise里封裝一個異步操作。
promise封裝:
// 點(diǎn)擊事件 btn.addEventListener('click',function(){ const p = new Promise((resolve,reject) => { //創(chuàng)建對象 const xhr = new XMLHttpRequest(); //初始化 xhr.open('GET', "http://poetry.apiopen.top/sentences"); //發(fā)送 xhr.send(); //處理響應(yīng)結(jié)果 xhr.onreadystatechange = function () { if (xhr.readyState === 4) { if (xhr.status >= 200 && xhr.status < 300) { //輸出響應(yīng)體 resolve(xhr.response); } else { //輸出響應(yīng)狀態(tài)碼 reject(xhr.status); } } } }) p.then(value=>{ console.log(value); },reason=>{ //控制臺輸出警告信息 console.warn(reason); }) })
若把接口寫錯:
五:Promise封裝ajax請求:
跟上一步差不多。就是把其封裝在一個sendAJAX()的自定義函數(shù)里。
function sendAJAX(url) { return new Promise((resolve, reject) => { //創(chuàng)建對象 const xhr = new XMLHttpRequest(); xhr.responseType = 'json'; //初始化 xhr.open('GET', url); //發(fā)送 xhr.send(); //處理響應(yīng)結(jié)果 xhr.onreadystatechange = function () { if (xhr.readyState === 4) { if (xhr.status >= 200 && xhr.status < 300) { //輸出響應(yīng)體 resolve(xhr.response); } else { //輸出響應(yīng)狀態(tài)碼 reject(xhr.status); } } } }); } sendAJAX("http://poetry.apiopen.top/sentences") .then(value=>{ console.log(value); },reason=>{ //控制臺輸出警告信息 console.warn(reason); })
六:promise的狀態(tài)改變:
promise狀態(tài)表示實(shí)例對象的一個屬性【PromiseState】。包括以下值:
(1)pending 未決定的
(2)resolved 或 fullfilled 成功
(3)rejected 失敗
Promise對象的值表示實(shí)例對象的另一個屬性【PromiseResult】。保存著對象【成功/失敗】的結(jié)果。而其狀態(tài)改變只有以下兩種可能:
(1)pending 變?yōu)閞esolved
(2)pending 變?yōu)?rejected
注:一個promise對象只能改變一次,無論成功或失敗都會有一個結(jié)果數(shù)據(jù),成功的稱為 value , 失敗的稱為 reason 。
七:Promise基本流程圖:
八:Promise的API 使用:
(1)executor 函數(shù):執(zhí)行器 (resolve,reject)=> {}。
(2)resolve 函數(shù):內(nèi)部定義成功時調(diào)用函數(shù) value => {} 。
(3)reject 函數(shù):內(nèi)部定義失敗時調(diào)用函數(shù) reason => {} 。
注意:Promise內(nèi)部會立同步即調(diào)用executor,異步操作在執(zhí)行器里執(zhí)行。
(1) onResolved 函數(shù):成功的回調(diào)函數(shù) (value) => {}
(2) onRejected 函數(shù):失敗的回調(diào)函數(shù) (reason) => {}
注:指定用于得到成功value的成功回調(diào)和用于得到失敗reason的失敗回調(diào)是返回一個新的promise對象。
onRejected.函數(shù): 失敗的回調(diào)函數(shù)**(reason)=> {}**
注:只是失敗的調(diào)用。then()的語法糖,相當(dāng)于: then(undefined, onRejected)。
value: 成功的數(shù)據(jù)或promise對象
注:如果傳入的參數(shù)為非Promise類 型的對象,則返回的結(jié)果為成功promise對象,如果傳入的參數(shù)為Promise 對象,則參數(shù)的結(jié)果決定了resolve 的結(jié)果。
reason: 失敗的原因
注:無論傳入啥只返回一個失敗的promise對象。
promises: 包含n個promise的數(shù)組
注:返回一個新的promise,只有所有的promise都成功才成功,只要有一個失敗了就直接失敗。失敗了返回那個失敗值。
promises: 包含n個promise的數(shù)組
注:返回一個新的promise,第一個完成的promise的結(jié)果狀態(tài)就是最終的結(jié)果狀態(tài)。
來一個例子:
let p1 = new Promise((resolve,reject)=>{ setTimeout(()=>{ resolve('yes'); },1000) }) let p2 = Promise.resolve('success'); let p3 = Promise.resolve('come'); const result = Promise.race([p1,p2,p3]); console.log(result);
九:使用Promise面臨的關(guān)鍵問題:
(1) resolve(value): 如果當(dāng)前是pending就會變?yōu)閞esolved。
(2) reject(reason): 如果當(dāng)前是pending就會變?yōu)閞ejected。
(3)拋出異常 throw :如果當(dāng)前是pending就會變?yōu)閞ejected。
let p1 = new Promise((resolve,reject)=>{ // resolve('success'); // reject('error'); // throw 'error'; })
當(dāng)promise改變?yōu)閷?yīng)狀態(tài)時都會調(diào)用。
let p = new Promise((resolve,reject)=>{ resolve('success'); }) // 第一次回調(diào) p.then(value=>{ console.log("yes"); }) // 第二次回調(diào) p.then(value=>{ console.log("oh yes"); })
3.改變 promiseT狀態(tài)和指定回調(diào)函數(shù)誰先誰后?
(1)都有可能, 正常情況下是先指定回調(diào)再改變狀態(tài),但也可以先改狀態(tài)再指定回調(diào)
(2)如何先改狀態(tài)再指定回調(diào)?
①在執(zhí)行 器中直接調(diào)用resolve(/reject();
②延遲更 長時間才調(diào)用then();
(3)什么時候才能得到數(shù)據(jù)?
①如果先指定的回調(diào), 那當(dāng)狀態(tài)發(fā)生改變時,回調(diào)函數(shù)就會調(diào)用,得到數(shù)據(jù)
②如果先改變的狀態(tài), 那當(dāng)指定回調(diào)時,回調(diào)函數(shù)就會調(diào)用,得到數(shù)據(jù)
(1)簡單表達(dá): then()指定的回調(diào)函數(shù)執(zhí)行的結(jié)果決定。
(2)詳細(xì)表達(dá):
*如果拋出異常, 新promise變?yōu)閞ejected, reaon為拋出的異常。
*如果返回的是非prormise的任意值,新promise變?yōu)閞esolved, value為返回的值。
*如果返回的是另一個新promise,此promise的結(jié)果就會成為新promise的結(jié)果。
隨筆:
let p = new Promise((resolve,reject) => { // resolve('success'); // reject('No'); // throw 'oh no'; }); let result = p.then(value => { console.log(value); }, reason => { console.warn(reason); }); console.log(result);
(1) promise 的then()返回一個新的promise,可以開成then()的鏈?zhǔn)秸{(diào)用。
(2)通過then的鏈?zhǔn)秸{(diào)用串連多個同步/異步任務(wù)。
let p =new Promise((resolve,reject) => { resolve("yes"); }) p.then(value => { return new Promise((resolve,reject)=>{ resolve("oh yes~"); }); }).then(value => { console.log(value); })
輸出結(jié)果:
oh yes~
let p =new Promise((resolve,reject) => { resolve("yes"); }) p.then(value => { return new Promise((resolve,reject)=>{ resolve("oh yes~"); }); }).then(value => { console.log(value); }).then(value => { console.log(value); })
輸出結(jié)果:
oh yes~
undefined
(1)當(dāng)使用promise的then鏈?zhǔn)秸{(diào)用時,可以在最后指定失敗的回調(diào)。
(2)前面任何操作出 了異常,都會傳到最后失敗的回調(diào)中處理。
let p =new Promise((resolve,reject) => { setTimeout(()=>{ resolve("yes"); },1000); }) p.then(value => { throw 'oh No'; }).then(value => { console.log("123"); }).then(value => { console.log("456"); }).catch(reason=>{ console.warn(reason); })
輸出結(jié)果:
oh No
(1)當(dāng)使用promise的then鏈?zhǔn)秸{(diào)用時,在中間中斷,不再調(diào)用后面的回調(diào)函數(shù)。
(2)辦法:在回調(diào)函數(shù)中返回一個pendding狀態(tài)的promise對象。
未中斷:
let p =new Promise((resolve,reject) => { setTimeout(()=>{ resolve("yes"); },1000); }) p.then(value => { console.log("789"); }).then(value => { console.log("123"); }).then(value => { console.log("456"); }).catch(reason=>{ console.warn(reason); })
輸出結(jié)果:
789
123
456
中斷:
let p =new Promise((resolve,reject) => { setTimeout(()=>{ resolve("yes"); },1000); }) p.then(value => { console.log("789"); return new Promise(()=>{}); }).then(value => { console.log("123"); }).then(value => { console.log("456"); }).catch(reason=>{ console.warn(reason); })
輸出結(jié)果:
789
十:Promise的自定義封裝:
封裝:
class Promise{ //構(gòu)造方法 constructor(executor) { //添加狀態(tài)屬性與結(jié)果值屬性 this.PromiseState = 'pending'; this.PromiseResult = null; // 定義callback屬性,保存pending狀態(tài)的回調(diào)函數(shù) this.callbacks = []; //保存實(shí)例對象的this值 const that = this; //自定義resolve函數(shù),名字不一定用resolve function resolve(data) { //判斷狀態(tài)是否修改過 if (that.PromiseState !== 'pending') return; //改變狀態(tài)屬性 that.PromiseState = 'fulfilled'; // 或者 resolve //改變結(jié)果值屬性 that.PromiseResult = data; //異步任務(wù)成功后執(zhí)行回調(diào)函數(shù) setTimeout(() => { that.callbacks.forEach(item => { item.onResolved(data); }) }); } //自定義reject函數(shù) function reject(data) { //判斷狀態(tài)是否修改過,改過就直接返回 if (that.PromiseState !== 'pending') return; //改變狀態(tài)屬性 that.PromiseState = 'rejected'; //改變結(jié)果值屬性 that.PromiseResult = data; //異步任務(wù)失敗后執(zhí)行回調(diào)函數(shù) setTimeout(() => { that.callbacks.forEach(item => { item.onRejected(data); }) }); } try{ //同步調(diào)用【執(zhí)行器函數(shù)】 executor(resolve,reject); }catch(e){ //更改Promise對象為失敗 reject(e); } } //then方法封裝 then(onResolved,onRejected){ const that = this; //判斷回調(diào)參數(shù)是否存在 if(typeof onRejected !== 'function'){ onRejected = reason =>{ throw reason; } } if(typeof onResolved !== 'function'){ onResolved = value => value; } return new Promise((resolve, reject) => { //封裝重復(fù)的部分 function callback(type){ try { //將結(jié)果值傳入 let result = type(that.PromiseResult); //判斷 if (result instanceof Promise) { //如果是Promise對象 result.then(v => { resolve(v); }, r => { reject(r); }) } else { //結(jié)果對象狀態(tài)為【成功】 resolve(result); } } catch (e) { reject(e); } } //如果Promise狀態(tài)為fulfilled回調(diào)這個函數(shù) if (this.PromiseState === 'fulfilled') { setTimeout(()=>{ callback(onResolved); }); } //如果Promise狀態(tài)為rejected回調(diào)這個函數(shù) if (this.PromiseState === 'rejected') { setTimeout(()=>{ callback(onRejected); }); } //如果Promise狀態(tài)為pending,保存回調(diào)函數(shù) if (this.PromiseState === 'pending') { this.callbacks.push({ onResolved: function () { callback(onResolved); }, onRejected: function () { callback(onRejected); } }) } }) } //catch 方法 catch(onRejected){ return this.then(undefined,onRejected); } //resolve方法 static resolve(value){ //返回promise對象 return new Promise((resolve,reject) =>{ if(value instanceof Promise){ value.then(v=>{ resolve(v); },r=>{ reject(r); }) }else{ resolve(value); } }) } //reject方法 static reject(reason){ return new Promise((resolve,reject)=>{ reject(reason); }); } //all方法 static all(promises) { return new Promise((resolve, reject) => { //添加變量 let count = 0; // 存放成功結(jié)果數(shù)組 let arr = []; //遍歷全部 for (let i = 0; i < promises.length; i++) { promises[i].then(v => { //能進(jìn)到證明其為成功 count++; //保存成功結(jié)果 arr[i] = v; //如果全部成功 if (count === promises.length) { //狀態(tài)為成功 resolve(arr); } }, r => { //能進(jìn)到證明其為失敗 reject(r); }); } }); } //race方法 static race(promises) { return new Promise((resolve, reject) => { //遍歷全部 for (let i = 0; i < promises.length; i++) { promises[i].then(v => { //能進(jìn)到證明其為成功 //狀態(tài)為成功 resolve(v); }, r => { //能進(jìn)到證明其為失敗 reject(r); }) } }); } }
十一:async函數(shù):
MDN文檔
1.函數(shù)的返回值為promise對象。
2.promise對象的結(jié)果由async函數(shù)執(zhí)行的返回值決定。
3.其實(shí)跟 then()方法返回結(jié)果是一樣一樣的。
async function main(){ return '123'; } let res = main(); console.log(res);
如:
async function main(){ return new Promise((resolve,reject)=>{ reject('NO'); }); } let res = main(); console.log(res);
async function main(){ return new Promise((resolve,reject)=>{ reject('NO'); }); } let res = main(); console.log(res);
十二.await表達(dá)式:
MDN文檔
1.await右側(cè)的表達(dá)式一般為promise對象,但也可以是其它的值。
2.如果表達(dá)式是promise對象,await返回的是promise成功的值。
3.如果表達(dá)式是其它值,直接將此值作為await的返回值。
注意:
1.await 必須寫在async函數(shù)中,但async 函數(shù)中可以沒有await 。
2.如果await的promise失敗了,就會拋出異常,需要通過try…catch捕獲處理。
async function works(){ let p = new Promise((resolve,reject)=>{ resolve('oh yes') }) let res = await p; console.log(res); } works();
結(jié)果:
oh yes
async function works(){ let p = new Promise((resolve,reject)=>{ resolve('oh yes') }) // let res = await p; let res = await 100; console.log(res); } works();
結(jié)果:
100
async function works(){ let p = new Promise((resolve,reject)=>{ // resolve('oh yes') reject('err'); }) try{ let res = await p; }catch(e){ console.log(e); } } works();
十三.async與await結(jié)合發(fā)生ajax請求:
效果: 點(diǎn)擊按鈕獲取一句名言。
十四.總結(jié):
我只想說…
【W(wǎng)EB前端大作戰(zhàn)】火熱進(jìn)行中:https://bbs.huaweicloud.com/blogs/255890
JavaScript VsCode web前端
版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時內(nèi)刪除侵權(quán)內(nèi)容。