1.什么是Promise?
Promise是JS異步編程中的重要概念,異步抽象處理對象,是目前比較流行Javascript異步編程解決方案之一。
2.對于幾種常見異步編程方案?(暫不說generator函數(shù)和async函數(shù))
1.回調函數(shù);
2.事件監(jiān)聽
3.發(fā)布/訂閱模式
4.Promise對象
3.方案對比
這里為了突出Promise在異步編程的優(yōu)勢,我主要使用了回調函數(shù)和Promise作出對比
1.回調函數(shù)
我們接觸比較多的回調函數(shù),我舉個例子,我們們用Jquery的ajax獲取數(shù)據(jù)時 都是以回調函數(shù)方式獲取的數(shù)據(jù)
$.get(url, (data) => {
console.log(data)
)
如果說 當我們需要發(fā)送多個異步請求 并且每個請求之間需要相互依賴 那這時 我們只能 以嵌套方式來解決 形成 "回調地獄"
$.get(url, data1 => {
console.log(data1)
$.get(data1.url, data2 => {
console.log(data1)
$.get(data2.url, data3 => {
console.log(data2)
.....
})
})
})
這樣一來,在處理越多的異步邏輯時,就需要越深的回調嵌套,導致代碼書寫順序與執(zhí)行順序不一致,不利于閱讀與維護,不利于bug的修復和新需求的加入等等。
2.Promise方案
這里我們使用Promise來解決這個callback-hell問題。
const asyncRequest = url => {
return new Promise((resolve,reject)=>{
$.get(url, data => {
resolve(data)
});
})
}
首先對Jq的請求函數(shù)進行Promise封裝
// 請求data1
asyncRequest(url)
.then(data1 => {
return request(data1.url);
})
.then(data2 => {
return request(data2.url);
})
.then(data3 => {
console.log(data3);
})
.catch(err => throw new Error(`error of ${err} happen in asyncRequest function`));
經過Promise處理的Jq請求函數(shù),在處理上述中回調地獄情形是如此優(yōu)勢巨大,整個請求下來,邏輯非常清晰和有利于代碼的維護。
接下來為了更加深入地了解Promise,我們不如造一個屬于自己的Promise吧,不過這個Promise不會完全實現(xiàn)和PromiseA+規(guī)范的Promise,這里我們只需要實現(xiàn)我們平時使用的Promise中的靜態(tài)resolve,reject函數(shù),原型對象的then函數(shù),catch函數(shù),實現(xiàn)鏈式調用,而race和all函數(shù)這些附加性質的函數(shù),我們有時間再去實現(xiàn),我們先簡單實現(xiàn)上述目標!
等等,實現(xiàn)一個Promise之前,我們先了解Promise的特性,不然你不知道需求,寫什么代碼?。。?/p>
3.Promise特性
1.Promise 是一個構造函數(shù), new Promise 返回一個 promise對象 接收一個excutor執(zhí)行函數(shù)作為參數(shù), excutor有兩個函數(shù)類型形參resolve reject
const promise = new Promise((resolve, reject) => {
// 異步處理
// 處理結束后、調用resolve 或 reject
});
2.Promise是一個狀態(tài)機,有三種不同的狀態(tài)。
- pending
- fulfilled
- rejected
(1) promise 對象初始化狀態(tài)為 pending
(2) 當調用resolve(成功),會由pending => fulfilled
(3) 當調用reject(失敗),會由pending => rejected
注意promsie狀態(tài) 只能由 pending => fulfilled/rejected, 一旦修改就不能再變
3.promise對象方法
(1) then方法注冊 當resolve(成功)/reject(失敗)的回調函數(shù)
// onFulfilled 是用來接收promise成功的值
// onRejected 是用來接收promise失敗的原因
promise.then(onFulfilled, onRejected);
(2) resolve(成功) onFulfilled會被調用
const promise = new Promise((resolve, reject) => {
resolve('fulfilled'); // 狀態(tài)由 pending => fulfilled
});
promise.then(result => { // onFulfilled
console.log(result); // 'fulfilled'
}, reason => { // onRejected 不會被調用
})
(3) reject(失敗) onRejected會被調用
const promise = new Promise((resolve, reject) => {
reject('rejected'); // 狀態(tài)由 pending => rejected
});
promise.then(result => { // onFulfilled 不會被調用
}, reason => { // onRejected
console.log(reason); // 'rejected'
})
(4) promise.catch
在鏈式寫法中可以捕獲前面then中發(fā)送的異常
promise.catch(onRejected)
相當于
promise.then(null, onRrejected);
// 注意
// onRejected 不能捕獲當前onFulfilled中的異常
promise.then(onFulfilled, onRrejected);
// 可以寫成:
promise.then(onFulfilled)
.catch(onRrejected);
(5)鏈式調用
promise.then方法每次調用都返回一個新的promise對象所以可以鏈式寫法
function taskA() {
console.log("Task A");
}
function taskB() {
console.log("Task B");
}
function onRejected(error) {
console.log("Catch Error: A or B", error);
}
var promise = Promise.resolve();
promise
.then(taskA)
.then(taskB)
.catch(onRejected) // 捕獲前面then方法中的異常
(6)Promise的靜態(tài)方法
- Promise.resolve 返回一個fulfilled狀態(tài)的promise對象
Promise.resolve('hello').then(function(value){
console.log(value);
});
Promise.resolve('hello');
// 相當于
const promise = new Promise(resolve => {
resolve('hello');
});
(2) Promise.reject 返回一個rejected狀態(tài)的promise對象
Promise.reject(24);
new Promise((resolve, reject) => {
reject(24);
});
(3) Promise.all 接收一個promise對象數(shù)組為參數(shù)
只有全部為resolve才會調用 通常會用來處理 多個并行異步操作
const p1 = new Promise((resolve, reject) => {
resolve(1);
});
const p2 = new Promise((resolve, reject) => {
setTimeout(()=>{
resolve(2);
},1000)
});
const p3 = new Promise((resolve, reject) => {
reject(3);
});
Promise.all([p1, p2, p3]).then(data => {
console.log(data); // [1, 2, 3] 結果順序和promise實例數(shù)組順序是一致的
}, err => {
console.log(err);
});
(4) Promise.race 接收一個promise對象數(shù)組為參數(shù)
Promise.race 只要有一個promise對象進入 FulFilled 或者 Rejected 狀態(tài)的話,就會繼續(xù)進行后面的處理。
function timerPromisefy(delay) {
return new Promise(function (resolve, reject) {
setTimeout(function () {
resolve(delay);
}, delay);
});
}
Promise.race([
timerPromisefy(10),
timerPromisefy(20),
timerPromisefy(30)
]).then(function (values) {
console.log(values); // 10
});
Promise特征總結
1.Promise是一個構造函數(shù),接受一個函數(shù)作為參數(shù),而該函數(shù)兩個函數(shù)類型形參resolve reject,這兩函數(shù)是構造函數(shù)內部提供。
2.Promise有三種狀態(tài)pending,fulfilled,rejected且狀態(tài)不能逆轉
3.Promise對象有三個方法then,resolve,reject,catch
4.Promise能夠進行鏈式調用
5.Promise靜態(tài)方法resolve,reject,all,rece。
需求準備好,開始早一個Promise!(all,race暫不實現(xiàn),另外不對thenable對象,和內部可能存在的其他Promise的情形作處理,先通過數(shù)字,字符串,對象等情況)
go on
首先我們要將下面這個回調函數(shù)做做處理變成Promise形式
function doSomething(cb) {
cb();
}
biubiubiu
function doSomething(){
return {
then(cb) {
cb()
}
}
}
好得到這段毫無意義的代碼,不過看上去好像有點樣式...!我們目前還沒有觸及 Promise 背后的核心概念,但這也是個小小的開始。
好繼續(xù)改造
上面代碼:
function MyPromise(){
return {
then(cb) {
cb()
}
}
}
改造代碼:
function MyPromise(excutor){
let callback = null;
this.then = function(cb) {
callback = cb
}
function resolve(val) {
callback(val)
}
try{
excutor(resolve)
}catch(error){
throw new Error(error)
}
}
這里改造后的代碼,好像有模有樣了,但是這里就遇到一個問題:如果你逐行執(zhí)行代碼,就會發(fā)現(xiàn)resolve() 函數(shù)在then() 函數(shù)之前被調用,這就意味著resolve() 被調用的時候,callback 還是 null。讓我們用一個 hack 來干掉這個問題,引入setTimeout處理一下,代碼如下所示:
function MyPromise(excutor){
let callback = null;
this.then = function(cb) {
callback = cb
}
function resolve(val) {
setTimeout(()=>{
callback(val)
},0)
}
try{
excutor(resolve)
}catch(error){
throw new Error(error)
}
}
測試代碼:
console.log(0)
var a = new MyPromise((resolve,reject)=>{
console.log(1);
resolve(2);
})
a.then(val=>{
console.log(val)
})
console.log(3)
結果:0,1,3,2
做到這里,程序的執(zhí)行順序和我們平常的Promise順序一致唉!不過還存在非常多的問題!
- 沒有鏈式調用
- 沒有Promise狀態(tài)
- 沒有reject函數(shù)作錯誤返回,catch函數(shù)做錯誤捕捉等等...
好繼續(xù)改造
// promise 三個狀態(tài)
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";
//這里用用es6的語法
class MyPromise {
constructor(excutor) {
if(typeof excutor !== 'function') {
throw new Error(`${excutor} is not a function`);
}
let that = this;
this.state = PENDING//fulfilled reject
this.value = undefined;
this.reason = undefined;
this.onFulfilledCallbacks = [];//紀錄成功的回調,就是then的第一個參數(shù)
this.onRejectedCallbacks = [];//紀錄成功的回調,就是then的第二個參數(shù)
function resolve(value) {
setTimeout(()=>{
that.state = FULFILLED;
that.value =value;
that.onFulfilledCallbacks.forEach((cb,idx)=>{
cb.call(this,that.value);
})
})
}
function reject(reason) {
setTimeout(()=>{
that.state = REJECTED;
that.reason = reason;
that.onRejectedCallbacks.forEach((cb,idx)=>{
cb.call(this,that.reason);
})
},0)
}
try {
excutor(resolve,reject);
} catch (error) {
reject(error);
}
}
then(onFulfilled,onRejected) {
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
onRejected = typeof onRejected === 'function' ? onRejected : reason=>{
throw new Error(reason)
}
return new MyPromise((resolve,reject)=>{
/*
*這里是箭頭函數(shù),所以這里this.onFulfilledCallbacks和*this.onRejectedCallbacks是指向外層的成功和失敗回調數(shù)據(jù),
*假如是function的形式,就需要提前定義這個**參數(shù)函數(shù)**然后通過*bind()函數(shù)綁定this的指向
*/
this.onFulfilledCallbacks.push(value)=>{
try {
let newValue = onFulfilled(value);
resolve(newValue);
} catch (error) {
reject(error)
}
}
this.onRejectedCallbacks.push(value=>{
try {
let reason = onRejected(value);
reject(reason)
} catch (error) {
reject(error)
}
})
})
}
}
測試代碼:
console.log(0)
var a = new SuperPromise((resolve,reject)=>{
console.log(1)
resolve(2)
})
a.then((val)=>{
console.log(val)
return (3)
}).then(val=>{
console.log(val)
})
console.log(4)
結果//0,1,4,2,3
好,這里我們實現(xiàn)了then的鏈式調用,這一看,我們只實現(xiàn)了原型上一個then函數(shù),接下來我們繼續(xù)實現(xiàn)catch,和靜態(tài)resolve,靜態(tài)reject
go on
上面我們提到catch函數(shù):
promise.catch(onRejected)
相當于
promise.then(null, onRrejected);
但是有一點不同的是,onRrejected不是不能捕捉到onFulfilled中的異常。
class MyPromise {
constructor(excutor) {
if(typeof excutor !== 'function') {
throw new Error(`${excutor} is not a function`);
}
let that = this;
this.state = PENDING//fulfilled reject
this.value = undefined;
this.reason = undefined;
this.onFulfilledCallbacks = [];//紀錄成功的回調,就是then的第一個參數(shù)
this.onRejectedCallbacks = [];//紀錄成功的回調,就是then的第二個參數(shù)
function resolve(value) {
setTimeout(()=>{
that.state = FULFILLED;
that.value =value;
that.onFulfilledCallbacks.forEach((cb,idx)=>{
cb.call(this,that.value);
})
})
}
function reject(reason) {
setTimeout(()=>{
that.state = REJECTED;
that.reason = reason;
that.onRejectedCallbacks.forEach((cb,idx)=>{
cb.call(this,that.reason);
})
},0)
}
try {
excutor(resolve,reject);
} catch (error) {
reject(error);
}
}
then(onFulfilled,onRejected) {
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
onRejected = typeof onRejected === 'function' ? onRejected : reason=>{
throw new Error(reason)
}
if(this.state === PENDING) {
return new MyPromise((resolve,reject)=>{
this.onFulfilledCallbacks.push(value=>{
try {
let newValue = onFulfilled(value);
resolve(newValue);
} catch (error) {
reject(error)
}
})
this.onRejectedCallbacks.push(value=>{
try {
let newValue = onRejected(value);
reject(newValue)
} catch (error) {
reject(error)
}
})
})
}
}
catch(onRejected) {
return this.then(null,onRejected)
}
}
測試代碼:
var b = new MyPromise((resolve,reject)=>{
reject('error happen in SuperPromise')
})
b.then((val)=>{
console.log(val,'val')
return val;
})
.catch(err=>{
console.log(err)
})
-------
var c = new MyPromise((resolve,reject)=>{
resolve(2)
})
c.then((val)=>{
console.log(val,'val')
throw new Error('some error in then')
})
.catch(err=>{
console.log(err)
})
接下黎啊繼續(xù)實現(xiàn)靜態(tài)resolve和reject
class MyPromise {
constructor(excutor) {
if(typeof excutor !== 'function') {
throw new Error(`${excutor} is not a function`);
}
let that = this;
this.state = PENDING//fulfilled reject
this.value = undefined;
this.reason = undefined;
this.onFulfilledCallbacks = [];//紀錄成功的回調,就是then的第一個參數(shù)
this.onRejectedCallbacks = [];//紀錄成功的回調,就是then的第二個參數(shù)
function resolve(value) {
setTimeout(()=>{
that.state = FULFILLED;
that.value =value;
that.onFulfilledCallbacks.forEach((cb,idx)=>{
cb.call(this,that.value);
})
})
}
function reject(reason) {
setTimeout(()=>{
that.state = REJECTED;
that.reason = reason;
that.onRejectedCallbacks.forEach((cb,idx)=>{
cb.call(this,that.reason);
})
},0)
}
try {
excutor(resolve,reject);
} catch (error) {
reject(error);
}
}
then(onFulfilled,onRejected) {
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
onRejected = typeof onRejected === 'function' ? onRejected : reason=>{
throw new Error(reason)
}
if(this.state === PENDING) {
return new MyPromise((resolve,reject)=>{
this.onFulfilledCallbacks.push(value=>{
try {
let newValue = onFulfilled(value);
resolve(newValue);
} catch (error) {
reject(error)
}
})
this.onRejectedCallbacks.push(value=>{
try {
let newValue = onRejected(value);
reject(newValue)
} catch (error) {
reject(error)
}
})
})
}
}
catch(onRejected) {
return this.then(null,onRejected)
}
//這兩個就比較簡單,我們回憶一下,當我們Promise.resolve(),就會馬上進入成功回調,相反Promise.reject(),就馬上進入失敗回調
static resolve(val){
return new MyPromise((resolve,reject)=>{
resolve(val)
})
}
static reject(reason){
return new MyPromise((resolve,reject)=>{
reject(reason)
})
}
}
測試代碼:
MyPromise.resolve(2).then(val=>{
console.log(val)
return 3
}).then(val=>{
console.log(val)
})
---
MyPromise.reject('some error happen in static reject')
.catch(err => console.log(err))
以上兩段代碼測試通過,完美!!!
好,我們再看看我們的需求
(all,race暫不實現(xiàn),另外不對thenable對象,和內部可能存在的其他Promise的情形作處理,先通過數(shù)字,字符串,對象等情況)
OK這里已經實現(xiàn)的Promise已經實現(xiàn)了我們上述的請求,如果看客跟著我一起去實現(xiàn)一個Promise,是不是會有一些成就感呢?說笑,這節(jié)說到這里。good night