javascript中的糖衣語法Promise對(duì)象詳解
知道Promise對(duì)象的你,知道在使用Promise對(duì)象的時(shí)候,我們?nèi)绾巫龅皆谑褂弥腥玺~得水嘛?10分鐘帶你迅速上手!
一、Promise的誕生
1、回調(diào)地獄
最初javascript的異步實(shí)現(xiàn)就是使用回調(diào)函數(shù)?;卣{(diào)地獄就是:一個(gè)函數(shù)需要等它的回調(diào)函數(shù)(或者回調(diào)和回調(diào)的回調(diào)...)執(zhí)行完畢之后再執(zhí)行。簡(jiǎn)單來說,回調(diào)函數(shù)里面嵌套回調(diào)函數(shù)。而因?yàn)榛卣{(diào)地獄的問題,Promise就出現(xiàn)了。我們看看什么是回調(diào)地獄:
// 回調(diào)地獄
//地獄回調(diào)
setTimeout(function () { //第一層
console.log(1);//等4秒打印1,然后執(zhí)行下一個(gè)回調(diào)函數(shù)
setTimeout(function () { //第二層
console.log(2);//等3秒打印2,然后執(zhí)行下一個(gè)回調(diào)函數(shù)
setTimeout(function () { //第三層
console.log(3);//等2秒打印3,然后執(zhí)行下一個(gè)回調(diào)函數(shù)
setTimeout(function () { //第四層
console.log(4);//等1秒打印4
}, 1000)
}, 2000)
}, 3000)
}, 4000)可看出回調(diào)地獄的特點(diǎn):
1.難以復(fù)用
2.堆棧信息被斷開
3.借助外層變量
回調(diào)地獄是為了讓我們代碼執(zhí)行順序的一種操作(解決異步),但是它會(huì)使我們的可讀性非常差。當(dāng)你有100個(gè),1000個(gè)...,代碼會(huì)不斷右移,不夠優(yōu)雅,也會(huì)影響性能。嵌套和縮進(jìn)只是回調(diào)地獄的一個(gè)梗而已,它導(dǎo)致的問題遠(yuǎn)非嵌套導(dǎo)致的可讀性降低而已。接下里我們今天的目的,就是將上面的回調(diào)地獄用Promise解決。
二、Promise的行為
1、Promise的語法
Promise的基本語法:Promise函數(shù)接收一個(gè)函數(shù)作為參數(shù),這個(gè)函數(shù)有兩個(gè)參數(shù),一個(gè)是成功函數(shù)(resolve),一個(gè)是失敗函數(shù)(reject)。Promise的.then接收兩個(gè)回調(diào)函數(shù),一個(gè)是成功函數(shù)的回調(diào),一個(gè)是失敗函數(shù)的回調(diào)。這兩個(gè)函數(shù)可選,不一定要提供
//Promise語法
let myPromise = new Promise(function(resolve, reject) {
// "Producing Code"(可能需要一些時(shí)間)
resolve(); // 成功時(shí)
reject(); // 出錯(cuò)時(shí)
});
// "Consuming Code" (必須等待一個(gè)兌現(xiàn)的承諾)
myPromise.then(
function(value) { /* 成功時(shí)的代碼 */ },
function(error) { /* 出錯(cuò)時(shí)的代碼 */ }
);特別注意:
(1)Promise對(duì)象中的狀態(tài)不會(huì)被外界干擾。狀態(tài)的改變?nèi)Q于異步的操作結(jié)果。
(2)Promise對(duì)象的狀態(tài)一旦被改變,就不會(huì)進(jìn)行再次改變。 例如:
let myPromise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('ok');//第一次狀態(tài)為成功
reject('no');//不會(huì)改變
})
}).then(
function (result) { console.log('resolved'); },//成功狀態(tài)執(zhí)行then后面的成功回調(diào)函數(shù)
function (error) { console.log('reject'); }
)
//resolved(3)Promise新建后就會(huì)立即執(zhí)行,Promise后面的.then是一個(gè)異步操作,在事件循環(huán)中叫做“微任務(wù)”。會(huì)放在同步代碼后面執(zhí)行。 例如:
let myPromise=new Promise((resolve,reject)=>{
console.log('Promise');//1
resolve();
})
.then(()=>{//這里是一個(gè)異步操作
console.log('succeed');//3
})
console.log('Promise resolved');//2
// Promise
// Promise resolved
// succeed2、Promise的方法
(1)Promise.prototype.then()
then方法的返回結(jié)果是新的Promise實(shí)例,對(duì)象狀態(tài)由回調(diào)函數(shù)的執(zhí)行結(jié)果決定。then方法后面還可以再調(diào)用另一個(gè)then方法,形成鏈條。采用鏈?zhǔn)降膖hen,可以指定一組按照次序調(diào)用的回調(diào)函數(shù)。
const p = new Promise((resolve, reject) => {
setTimeout(() => {
//設(shè)置 p 對(duì)象的狀態(tài)為失敗,并設(shè)置失敗的值
reject("出錯(cuò)啦!");
}, 1000)
});
p.then(
function(value){},
function(reason){console.log(reason);}
)
.then(()=>{
console.log(123)
});(2)Promise.prototype.catch()
catch()用于指定發(fā)生錯(cuò)誤時(shí)的回調(diào)函數(shù).例如:
const p = new Promise((resolve, reject) => {//p為Promise的實(shí)例
setTimeout(() => {
//設(shè)置 p 對(duì)象的狀態(tài)為失敗,并設(shè)置失敗的值
reject("出錯(cuò)啦!");//reject()方法的作用,等同于拋出錯(cuò)誤
}, 1000)
});
// p.then(function(value){},function(reason){
// // console.error(reason);
// console.log(reason);
// });
?
p.catch(function (reason) {//相當(dāng)于上面的.then(...)
console.log(reason);//捕獲reject狀態(tài)的值
});建議總是使用catch()方法,而不使用then()方法的第二個(gè)參數(shù).
catch()還可以這樣使用:
const myPromise = new Promise(function(resolve, reject) {
throw new Error('出錯(cuò)啦');//從這拋出錯(cuò)誤,catch()指定的回調(diào)函數(shù)也可以捕獲
});
promise.catch(function(error) {
console.log(error);
});(3)Promise.prototype.finally()
finally()方法用于指定不管 Promise 對(duì)象最后狀態(tài)如何,都會(huì)執(zhí)行的操作。而且finally方法總是會(huì)返回原來的值。舉個(gè)例子:
function a(){
return new Promise((resolve,reject)=>{
resolve('ok')
},1000)
}
function b(){
return new Promise((resolve,reject)=>{
setTimeout(()=>{
reject('no')
},1500)
})
}
function c(){
setTimeout(()=>{
console.log('finally');
},500)
}
a()
.then((res)=>{
console.log(res);
return b()
})
.catch((err)=>{
console.log(err);
})
.finally(c)//參數(shù)不是回調(diào)函數(shù),如果是回調(diào)函數(shù)也不需要帶參
?
//ok
//no
//finally //說明finally()照樣執(zhí)行,有打印(4)Promise.resolve()
將現(xiàn)有對(duì)象轉(zhuǎn)為 Promise 對(duì)象,狀態(tài)為resolved。舉例如下:
let myString='hello';
console.log(myString);//hello
const myPromise=Promise.resolve(myString)//帶參
//等同于const myPromise=Promise.resolve('hello')
console.log(myPromise);//Promise { 'hello' }
myString=Promise.resolve();//不帶參,直接調(diào)用
console.log(myString);//Promise { undefined }
myString.then(result=>{//說明myString已經(jīng)是Promise對(duì)象了,只有Promise對(duì)象才有.then
console.log(result);//undefined
})(5)Promise.reject()
也會(huì)返回一個(gè)新的 Promise 實(shí)例,該實(shí)例的狀態(tài)為rejected。簡(jiǎn)單舉例:
// 以上代碼等于
const p=new Promise((resolve,reject)=>{
reject('error')
})
p.catch(error=>{
console.log(error);//error
})
// 或者
p.then(function(){},function(error){console.log(error);})//error
// 或者
p.then(null,function(error){console.log(error);})//error
// 或者
p.then(undefined,function(error){console.log(error);})//error(6)Promise.all()
all()是將多個(gè) Promise 實(shí)例,包裝成一個(gè)新的 Promise 實(shí)例。接收一個(gè)數(shù)組作為參數(shù),數(shù)組的每一項(xiàng)都是·Promise對(duì)象的實(shí)例。如果不是,會(huì)通過Promise.resolve()將參數(shù)轉(zhuǎn)為Promise實(shí)例,再進(jìn)行處理。all()用于將多個(gè) Promise 實(shí)例,包裝成一個(gè)新的 Promise 實(shí)例。
// promise.all()
const myPromise1=new Promise((resolve,reject)=>{
resolve('sure');
})
.then(result=>result)
const myPromise2=new Promise((resolve,reject)=>{
reject('cancel')
})
.then(result=>result)
.catch(error=>error)//myPromise2有自己的catch
//感興趣的小伙伴可以嘗試,如果刪除myPromise2.catch(...)后Promise.all([myPromise1,myPromise2])會(huì)如何?
Promise.all([myPromise1,myPromise2])//myPromise1,myPromise2都處于成功狀態(tài)
.then(result=>{console.log(result);})//走這里 [ 'sure', 'cancel' ]
.catch(error=>{console.log(error);})(7)Promise.race()
race()是將多個(gè) Promise 實(shí)例,包裝成一個(gè)新的 Promise 實(shí)例。接收一個(gè)數(shù)組作為參數(shù),數(shù)組的每一項(xiàng)都是·Promise對(duì)象的實(shí)例。如果不是,會(huì)通過promise.resolve()將參數(shù)轉(zhuǎn)為Promise實(shí)例,再進(jìn)行處理。只要參數(shù)的Promise實(shí)例有一個(gè)率先改變狀態(tài),則狀態(tài)改變。例如:
const myPromise2 = new Promise((resolve, reject) => {
setTimeout(()=>{
reject('cancel')
},3000)
//如果將時(shí)間改為<2000,Promise.race([myPromise1, myPromise2])走哪一步呢?
})
.then(result => result)
.catch(error => error)
const myPromise1 = new Promise((resolve, reject) => {
setTimeout(()=>{
resolve('sure');
},2000)//myPromise1比myPromise2更先改變狀態(tài)
})
.then(result => result)
?
?
Promise.race([myPromise1, myPromise2])
.then(result => { console.log(result); })//走這里,sure
.catch(error => { console.log(error); })簡(jiǎn)要說一下const p1=Promise.all([promise1,promise2,promise3]) 和const p2=Promise.race([promise1,promise2,promise3])的
區(qū)別:--前者Promise的實(shí)例狀態(tài)都為resolved時(shí),p1調(diào)用.then()里面成功時(shí)的回調(diào)函數(shù);如果實(shí)例狀態(tài)有一個(gè)為rejected,p1調(diào)用.then()里面失敗時(shí)的函數(shù)或者走.catch()
--后者注重時(shí)序,如果率先改變狀態(tài)的實(shí)例為resolved,則p2為reslove狀態(tài);否則,為reject狀態(tài)。
三、Promise的場(chǎng)景
1、Ajax請(qǐng)求
<head>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
</head>
<body>
<div class="name">
<audio id="audio" controls></audio>
</div>
<script>
function getSong() {
return new Promise((resolve, reject) => {
$.ajax({
url: 'https://www.fastmock.site/mock/c024e8920dd6003c63dcd9ed2bbf6cb9/music/music',
dataType: 'json',
success(res) {
console.log(res);
url = res[0].url;
}
})
resolve();
})
}
function playSong() {
let audio = document.getElementById('audio');
window.addEventListener('click', function () {
audio.src = url;
audio.play()
})
}
getSong().then(playSong())
</script>
</body>2、文件讀取
// 引入fs模塊
const fs=require('fs');
// 使用Promise封裝
const P=new Promise(function(resolve,reject){
fs.readFile('./text/2.md',(err,data)=>{
// 如果地址錯(cuò)誤,拋出異常
if(err) reject(err) ;
// 如果成功,輸出內(nèi)容
resolve(data);
});
});
P.then(function(value){
console.log(value.toString());
},function(reason){
console.log("defeat!!!!");
});3、圖片加載
<body>
<img src="http://m.imeitou.com/uploads/allimg/220514/5-220514101036.jpg" alt="風(fēng)景" id="myImage">
<script>
const preloadImage = function (path) {
return new Promise(function (resolve, reject) {
const image = new Image();
// 圖片加載成功
image.onload=()=>{
resolve(image)
}
// 圖片加載失敗
image.onerror=()=>{
reject('sorry,cannot loding picture')
};
image.src = path;
});
};
// 獲取圖片DOM節(jié)點(diǎn)
var preImage=document.getElementById('myImage')
// 圖片預(yù)加載
preloadImage('http://m.imeitou.com/uploads/allimg/220512/5-220512111J0-50.jpg')
.then(targetImage=>
// 點(diǎn)擊頁面切換圖片,讓圖片加載
window.onclick=function(){
setTimeout(()=>{
preImage.src=targetImage.src
},1000)
}
)
</script>
</body>4、函數(shù)封裝
//例如將微信小程序里的showModal進(jìn)行Promise封裝,那么在任何需要用到此函數(shù)的直接引入就很方便了
// promise形式 封裝 showModal
export const showModal=({content})=>{
return new Promise((resolve,reject)=>{
wx.showModal({
title:'提示',
content:content,
success: (result) => {
resolve(result);
},
fail: (err) => {
reject(err);
}
});
})
}四、Promise的短板
1.無法取消Promise,一旦新建它就會(huì)立即執(zhí)行,無法中途取消
2.如果不設(shè)置回調(diào)函數(shù),Promise內(nèi)部拋出的錯(cuò)誤,不會(huì)反映到外部
3.當(dāng)處于pending狀態(tài)時(shí),無法得知目前進(jìn)展到哪一個(gè)階段,是剛剛開始還是即將完成
總結(jié)
恭喜你看到文章末尾了,相信你可以寫出Promise方案解決第一個(gè)回調(diào)地獄的異步問題了,快去試試把!
到此這篇關(guān)于javascript中的糖衣語法Promise對(duì)象詳解的文章就介紹到這了,更多相關(guān)js promise對(duì)象內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
js實(shí)現(xiàn)有時(shí)間限制消失的圖片方法
這篇文章主要介紹了js實(shí)現(xiàn)有時(shí)間限制消失的圖片方法,實(shí)例分析了javascript操作setTimeout及圖片特效操作的相關(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-02-02
JavaScript 總結(jié)幾個(gè)提高性能知識(shí)點(diǎn)(推薦)
下面小編就為大家?guī)硪黄狫avaScript 總結(jié)幾個(gè)提高性能知識(shí)點(diǎn)(推薦)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-02-02
javascript設(shè)置和獲取cookie的方法實(shí)例詳解
這篇文章主要介紹了javascript設(shè)置和獲取cookie的方法,結(jié)合實(shí)例形式較為詳細(xì)的分析總結(jié)了JavaScript操作cookie簡(jiǎn)單實(shí)現(xiàn)數(shù)據(jù)存儲(chǔ)與讀取的相關(guān)技巧,需要的朋友可以參考下2016-01-01

