1,介紹
Promise是非同步程式設計的一種解決方案,比回撥函式和事件更合理且更強大。可以理解為一個容器,裡面儲存著某個未來才會結束的事件的結果。
2,特點
-
Promise物件有三種狀態:pending(進行中)、fulfilled(已成功)和rejected(已失敗),該狀態不受外界影響。
-
狀態可以從pending變為fulfilled,或者從pending變為rejected。一旦狀態改變,就不會再變。
3,缺點
-
Promise一旦新建它就會立即執行,無法中途取消
-
如果不設定回撥函式,Promise內部丟擲的錯誤,不會反應到外部
-
當處於pending狀態時,無法得知目前的進展是剛開始還是即將完成
4,基本用法
ES6
規定,Promise
物件是一個建構函式,用來生成Promise
例項,它接受一個函式作為引數,該函式的兩個函式引數分別是resolve
和reject
,resolve
的作用是將Promise
物件的狀態從未完成變為成功,reject
函式的作用是將Promise
物件的狀態從未完成變為失敗。
const promise = new Promise(function(resolve, reject) {
if (/* 非同步操作成功 */){
resolve(value);
} else {
reject(error);
}
});
5,then
Promise例項生成以後,可以用then
方法分別指定resolved
狀態和rejected
狀態的回撥函式(一般都只用第一個引數,reject
狀態交由catch
專門處理)
function timeout(ms) {
return new Promise((resolve, reject) => {
setTimeout(resolve('done'), ms);
});
}
timeout(100).then(
// resolve回撥
(value) => { console.log(value) },
// reject回撥
(error) => { console.log(error) }
);
Promise
新建後就會立即執行,但是then
方法是微任務,將在當前指令碼所有同步任務執行完才會執行。如下程式碼:首先輸出的是A
。然後才是then
方法指定的回撥函式,所以B
最後輸出。
let promise = new Promise(function(resolve, reject) {
console.log('A');
resolve();
});
promise.then(function() {
console.log('B');
});
console.log('C');
// 輸出順序 A C B
then
方法返回的是一個新的Promise
例項(不是原來那個Promise
例項)。因此可以採用鏈式寫法,即then
方法後面再呼叫另一個then
方法。
getJSON("/api")
.then(post => getJSON(post.commentURL))
.then(comments => console.log("resolve", comments))
如下程式碼:p2操作的結果是返回p1的非同步操作結果。這時p1的狀態就會傳遞給p2,也就是說,p1的狀態決定了p2的狀態。如果p1的狀態是pending,那麼p2的回撥函式就會等待p1的狀態改變;如果p1的狀態已經是resolved或者rejected,那麼p2的回撥函式將會立刻執行。
const p1 = new Promise(function (resolve, reject) {
setTimeout(() => reject(new Error('fail')), 3000)
})
const p2 = new Promise(function (resolve, reject) {
setTimeout(() => resolve(p1), 1000)
})
p2
.then(result => console.log(result))
.catch(error => console.log(error))
6,catch
和then
一樣,catch()
方法返回的也是一個Promise
物件,因此後面還可以接著呼叫then()
方法,如果沒有報錯,則會跳過catch()
方法。此時,要是後面的then()
方法裡面報錯,就與前面的catch()
無關了
const someAsyncThing = function() {
return new Promise(function(resolve, reject) {
// 下面一行會報錯,因為x沒有宣告
resolve(x + 2);
});
};
someAsyncThing()
.catch(function(error) {
console.log('oh no', error);
})
.then(function() {
console.log('carry on');
});
7,finally
finally()方法是ES2018引入標準的。用於指定不管 Promise 物件最後狀態如何,都會執行的操作。該方法總是會返回原來的值,並且不接受任何引數,這意味著沒有辦法知道前面的 Promise狀態到底是fulfilled還是rejected。
promise
.then(result => {···})
.catch(error => {···})
.finally(() => {···});
8,all()
Promise.all()
方法用於將多個Promise
例項,包裝成一個新的 Promise
例項。該方法接受一個陣列作為引數(可以不是陣列,但必須具有Iterator
介面,且返回的每個成員都是Promise
例項)
const p = Promise.all([p1, p2, p3]);
如上程式碼,p1
、p2
、p3
都是Promise
例項,如果不是,就會先呼叫Promise.resolve()
,將引數轉為Promise
例項,再進一步處理。
注意:
-
只有陣列裡所有的
Promise
都完成,Promise.all()
才會完成 -
如果陣列裡的
Promise
有一個失敗,Promise.all()
就會失敗,第一個失敗的Promise
例項會傳遞給Promise.all()
的回撥 -
如果作為引數的
Promise
例項,自己定義了catch
方法,那麼它一旦被rejected
,並不會觸發Promise.all()
的catch
方法。
9,race()
Promise.race()
方法同樣是將多個Promise
例項,包裝成一個新的Promise
例項,引數與Promise.all()
方法一樣。
const p = Promise.race([p1, p2, p3]);
race
的意思是速度,和字面意思一樣,誰快,就取誰作為結果。上面程式碼中,只要p1
、p2
、p3
之中有一個例項率先改變狀態,p
的狀態就跟著改變。率先改變的Promise
例項的返回值,就傳遞給p
的回撥函式。
10,allSettled()
Promise.allSettled()
方法接受一個陣列作為引數,陣列的每個成員都是一個Promise
物件,並返回一個新的Promise
陣列集合。
注意:只有等到引數陣列的所有Promise
物件都發生狀態變更(不管是fulfilled
還是rejected
),返回的Promise
物件才會發生狀態變更。
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('ok')
}, 1000)
})
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
reject('no')
}, 2000)
})
const p3 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('ok')
}, 3000)
})
const all = Promise.allSettled([p1, p2, p3])
.then(res => {
console.log(res)
})
11,any()
Promise.any()
方法接受一個陣列作為引數,陣列的每個成員都是一個Promise
物件,並返回一個新的Promise
陣列集合。
注意:只要引數陣列的Promise
物件有一個變成fulfilled
狀態,Promise.any()
就會變成fulfilled
狀態。只有引數陣列裡所有Promise
物件都變成rejected
狀態,Promise.any()
才會變成rejected
狀態。
const all = Promise.any([p1, p2, p3])
.then(res => {
console.log(res)
})
.catch(err => {
console.log(err)
})
12,現有物件轉為Promise物件
有時需要將現有物件轉為Promise
物件,Promise.resolve()
和Promise.reject()
就起到這個作用。
onst p = Promise.resolve('Hello');
p.then(function (s) {
console.log(s)
});
// 輸出 Hello
或者
Promise.reject('出錯了')
.catch(e => {
console.log(e)
})
// 輸出 出錯了
13,實戰用法
這裡列舉一些簡單的例子,還有很多用處。
13.1,小程式request
const Request = (options) =>{
let url = baseURL + options.url;
return new Promise((resolve, reject) => {
wx.request({
url,
data: options.data || {},
method: options.method || 'post',
responseType: options.responseType || '',
timeout: 15000,
success (res) {
if(res.statusCode === 200){
resolve(res.data);
}else{
FN.Toast(res.errMsg);
};
},
fail (res) {
FN.Toast("網路開小差了");
reject(res);
}
})
})
}
13.2,圖片載入
const preloadImage = function (path) {
return new Promise(function (resolve, reject) {
const image = new Image();
image.onload = resolve;
image.onerror = reject;
image.src = path;
});
}
13.3,封裝Toast
import { Message, MessageBox} from 'element-ui'
/**
* 提示框
* @param {String} text
*/
alert(text) {
return new Promise((resolve, reject) => {
MessageBox.alert(text, '溫馨提示', {
confirmButtonText: '確定',
callback: action => {
if (action === 'confirm'){
resolve(action)
} else {
reject(action)
}
}
})
})
}
14,手寫Promise
class myPromise {
constructor(executor) {
this.initState()
this.initBind()
try {
executor(this.resolve, this.reject)
} catch (error) {
this.reject(error)
}
}
initState() {
this.result = null
this.state = 'pending'
this.resolveCallback = []
this.rejectCallback = []
}
initBind() {
this.resolve = this.resolve.bind(this)
this.reject = this.reject.bind(this)
}
resolve(value) {
if (this.state !== 'pending') return
this.result = value
this.state = 'success'
while (this.resolveCallback.length) {
this.resolveCallback.shift()(this.result)
}
}
reject(value) {
if (this.state !== 'pending') return
this.result = value
this.state = 'error'
while (this.rejectCallback.length) {
this.rejectCallback.shift()(this.result)
}
}
then(onResolve, onReject) {
onResolve = typeof onResolve === 'function' ? onResolve : res => res
onReject = typeof onReject === 'function' ? onReject : res => res
switch (this.state) {
case 'pending':
this.resolveCallback.push(onResolve.bind(this))
this.rejectCallback.push(onReject.bind(this))
break
case 'success':
onResolve(this.result)
break
case 'error':
onReject(this.result)
break
}
}
catch(onReject) {
return this.then(null, onReject)
}
}
const fn = new myPromise((resolve, reject) => {
setTimeout((res) => {
resolve('成功')
}, 2000)
})
.then(res => {
console.log(res)
})
如果看了覺得有幫助的,我是@鵬多多,歡迎 點贊 關注 評論;END
公眾號
往期文章
- 使用nvm管理node.js版本以及更換npm淘寶映象源
- 超詳細!Vue-Router手把手教程
- vue中利用.env檔案儲存全域性環境變數,以及配置vue啟動和打包命令
- 微信小程式實現搜尋關鍵詞高亮
- 超詳細!Vue的九種通訊方式
- 超詳細!Vuex手把手教程
個人主頁