1.什么是Promise?
Promise是JS異步編程中的重要概念,異步抽象處理對象,是目前比較流行Javascript異步編程解決方案之一。
2.對于幾種常見異步編程方案?(暫不說generator函數和async函數)
1.回調函數;
2.事件監聽
3.發布/訂閱模式
4.Promise對象
3.方案對比
這里為了突出Promise在異步編程的優勢,我主要使用了回調函數和Promise作出對比
1.回調函數
我們接觸比較多的回調函數,我舉個例子,我們們用Jquery的ajax獲取數據時 都是以回調函數方式獲取的數據
$.get(url, (data) => {
console.log(data)
)
如果說 當我們需要發送多個異步請求 并且每個請求之間需要相互依賴 那這時 我們只能 以嵌套方式來解決 形成 "回調地獄"
$.get(url, data1 => {
console.log(data1)
$.get(data1.url, data2 => {
console.log(data1)
$.get(data2.url, data3 => {
console.log(data2)
.....
})
})
})
這樣一來,在處理越多的異步邏輯時,就需要越深的回調嵌套,導致代碼書寫順序與執行順序不一致,不利于閱讀與維護,不利于bug的修復和新需求的加入等等。
2.Promise方案
這里我們使用Promise來解決這個callback-hell問題。
const asyncRequest = url => {
return new Promise((resolve,reject)=>{
$.get(url, data => {
resolve(data)
});
})
}
首先對Jq的請求函數進行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請求函數,在處理上述中回調地獄情形是如此優勢巨大,整個請求下來,邏輯非常清晰和有利于代碼的維護。
接下來為了更加深入地了解Promise,我們不如造一個屬于自己的Promise吧,不過這個Promise不會完全實現和PromiseA+規范的Promise,這里我們只需要實現我們平時使用的Promise中的靜態resolve,reject函數,原型對象的then函數,catch函數,實現鏈式調用,而race和all函數這些附加性質的函數,我們有時間再去實現,我們先簡單實現上述目標!
等等,實現一個Promise之前,我們先了解Promise的特性,不然你不知道需求,寫什么代碼啊!!
3.Promise特性
1.Promise 是一個構造函數, new Promise 返回一個 promise對象 接收一個excutor執行函數作為參數, excutor有兩個函數類型形參resolve reject
const promise = new Promise((resolve, reject) => {
// 異步處理
// 處理結束后、調用resolve 或 reject
});
2.Promise是一個狀態機,有三種不同的狀態。
- pending
- fulfilled
- rejected
(1) promise 對象初始化狀態為 pending
(2) 當調用resolve(成功),會由pending => fulfilled
(3) 當調用reject(失敗),會由pending => rejected
注意promsie狀態 只能由 pending => fulfilled/rejected, 一旦修改就不能再變
3.promise對象方法
(1) then方法注冊 當resolve(成功)/reject(失敗)的回調函數
// onFulfilled 是用來接收promise成功的值
// onRejected 是用來接收promise失敗的原因
promise.then(onFulfilled, onRejected);
(2) resolve(成功) onFulfilled會被調用
const promise = new Promise((resolve, reject) => {
resolve('fulfilled'); // 狀態由 pending => fulfilled
});
promise.then(result => { // onFulfilled
console.log(result); // 'fulfilled'
}, reason => { // onRejected 不會被調用
})
(3) reject(失敗) onRejected會被調用
const promise = new Promise((resolve, reject) => {
reject('rejected'); // 狀態由 pending => rejected
});
promise.then(result => { // onFulfilled 不會被調用
}, reason => { // onRejected
console.log(reason); // 'rejected'
})
(4) promise.catch
在鏈式寫法中可以捕獲前面then中發送的異常
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的靜態方法
- Promise.resolve 返回一個fulfilled狀態的promise對象
Promise.resolve('hello').then(function(value){
console.log(value);
});
Promise.resolve('hello');
// 相當于
const promise = new Promise(resolve => {
resolve('hello');
});
(2) Promise.reject 返回一個rejected狀態的promise對象
Promise.reject(24);
new Promise((resolve, reject) => {
reject(24);
});
(3) Promise.all 接收一個promise對象數組為參數
只有全部為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實例數組順序是一致的
}, err => {
console.log(err);
});
(4) Promise.race 接收一個promise對象數組為參數
Promise.race 只要有一個promise對象進入 FulFilled 或者 Rejected 狀態的話,就會繼續進行后面的處理。
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是一個構造函數,接受一個函數作為參數,而該函數兩個函數類型形參resolve reject,這兩函數是構造函數內部提供。
2.Promise有三種狀態pending,fulfilled,rejected且狀態不能逆轉
3.Promise對象有三個方法then,resolve,reject,catch
4.Promise能夠進行鏈式調用
5.Promise靜態方法resolve,reject,all,rece。
需求準備好,開始早一個Promise!(all,race暫不實現,另外不對thenable對象,和內部可能存在的其他Promise的情形作處理,先通過數字,字符串,對象等情況)
go on
首先我們要將下面這個回調函數做做處理變成Promise形式
function doSomething(cb) {
cb();
}
biubiubiu
function doSomething(){
return {
then(cb) {
cb()
}
}
}
好得到這段毫無意義的代碼,不過看上去好像有點樣式...!我們目前還沒有觸及 Promise 背后的核心概念,但這也是個小小的開始。
好繼續改造
上面代碼:
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)
}
}
這里改造后的代碼,好像有模有樣了,但是這里就遇到一個問題:如果你逐行執行代碼,就會發現resolve() 函數在then() 函數之前被調用,這就意味著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
做到這里,程序的執行順序和我們平常的Promise順序一致唉!不過還存在非常多的問題!
- 沒有鏈式調用
- 沒有Promise狀態
- 沒有reject函數作錯誤返回,catch函數做錯誤捕捉等等...
好繼續改造
// promise 三個狀態
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的第一個參數
this.onRejectedCallbacks = [];//紀錄成功的回調,就是then的第二個參數
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)=>{
/*
*這里是箭頭函數,所以這里this.onFulfilledCallbacks和*this.onRejectedCallbacks是指向外層的成功和失敗回調數據,
*假如是function的形式,就需要提前定義這個**參數函數**然后通過*bind()函數綁定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
好,這里我們實現了then的鏈式調用,這一看,我們只實現了原型上一個then函數,接下來我們繼續實現catch,和靜態resolve,靜態reject
go on
上面我們提到catch函數:
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的第一個參數
this.onRejectedCallbacks = [];//紀錄成功的回調,就是then的第二個參數
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)
})
接下黎啊繼續實現靜態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的第一個參數
this.onRejectedCallbacks = [];//紀錄成功的回調,就是then的第二個參數
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暫不實現,另外不對thenable對象,和內部可能存在的其他Promise的情形作處理,先通過數字,字符串,對象等情況)
OK這里已經實現的Promise已經實現了我們上述的請求,如果看客跟著我一起去實現一個Promise,是不是會有一些成就感呢?說笑,這節說到這里。good night