一文讀懂Promise

什麼是Promise

$.ajax({
	success: (res) => {
		$.ajax({
            success: (res) => {
				$.ajax({
                    success: (res) => {
						//...
                    }
                })
            }
        })
	}
})
複製代碼

這就是典型的回調地獄,不只代碼臃腫,可讀性差,並且耦合度太高,不易維護。代碼沒法複用,還容易隱藏bug。前端

Promise規範的出現就是爲了解決這種問題。ajax

這裏強調一下,Promise是一種解決方案,它是一種規範。數組

ES6原生提供了Promise對象,在平常開發中常常會接觸到Promise相關操做,本文將介紹Promise的使用方法及相關原理,但願能有所幫助。promise

Promise有三種狀態:異步

  • Pending(等待態)
  • Fulfilled(成功態)
  • Rejected(失敗態)

Promise的特色是狀態只能由Pending轉換爲Fulfilled或Rejected,而且一旦改變再也不更改。函數

new Promise((resolve,reject) => {
	/*executor*/
})
複製代碼

Promise是一個帶有resolve,reject兩個參數的構造函數(executor)。這個構造函數在Promise建立的時候當即執行,resolve和reject兩個函數在被調用時,分別將Promise的狀態更改成fulfilled(成功態)和rejected(失敗態),並傳遞成功的值或失敗的緣由。executor內部一般會執行一些異步操做,當異步操做執行完畢時,可根據執行結果相應地調用resolve或reject(可能成功可能失敗)。若是executor拋出一個錯誤,那麼該Promise也將轉爲失敗態。測試

Promise原型上還具備then和catch方法,調用後返回一個Promise實例,所以能夠被鏈式調用。網站

每個Promise實例都帶有一個then方法,該方法有兩個參數(onFulfilled,onRejected),分別爲Promise成功和失敗時的回調。ui

let p = new Promise((resolve,reject) => {
	resolve('success');
})
p.then(value => {
	console.log(value);		//success
}).then(value => {
    console.log(value);		//沒有返回值時,undefined
})
複製代碼

當返回了一個值或者一個成功態的Promise時,then返回的Promise都是成功態,若沒有返回值時也是成功態,而回調函數的參數值爲undefindspa

當拋出一個錯誤或者返回一個失敗態Promise,then返回的Promise都是失敗態。

let p = new Promise((resolve,reject) => {
	reject('fail');
})
p.then(() => {
	
},err => {
	console.log(err);		//fail
});
複製代碼

then方法傳遞的成功/失敗函數,這兩個函數能夠返回一個promise;若是返回的是一個promise,則該promise狀態將做爲下一次then的結果。

let p = new Promise((resolve,reject) => {
	resolve('success')
});
let p2 = new Promise((resolve,reject) => {
    resolve('success2')
})
p.then(value => {
    return p2;				//p2的成功態將做爲下一次then的狀態
}).then(value => {
    console.log(value)
})
複製代碼

Promise.prototype.catch方法返回也是一個Promise,使用方式同then方法,用於處理異常捕獲狀況。該方法捕獲屬於就近捕獲原則,離錯誤最近的一個catch將會捕獲錯誤並進行處理,後續將再也不捕獲。

若失敗狀態被then方法中失敗函數回調觸發,則catch方法將不進行捕獲。

let p1 = new Promise((resolve,reject) => {
	resolve('success');
});
p1.then(value => {
    throw 'a new error';
}).catch(e => {
    console.log(e);		//a new error
}).then(() => {
    console.log('after a catch the chain is restored');
})
複製代碼

Promise.all方法返回一個新的Promise對象,在參數對象中全部的promise對象都成功時纔會觸發成功態,一旦有任意一個對象失敗則當即觸發該Promise對象的失敗態。當觸發成功態後,會把一個包含全部Promise返回值的數組按順序返回。若是觸犯失敗態,則會把第一個失敗的Promise對象錯誤信息返回。

let p1 = new Promise((resolve,reject) => {
    setTimeout(() => {
        resolve('p1')
    },0)
});
let p2 = new Promise((resolve,reject) => {
    setTimeout(() => {
        resolve('p2')
    },0)
});
let promise = Promise.all([p1,p2]).then(value => {
    console.log(value);		//['p1','p2']
})
複製代碼

Promise.race方法是把參數中任意第一個完成狀態轉換的Promise做爲成功或失敗狀態,並返回該Promise對象。

let p1 = new Promise((resolve,reject) => {
    setTimeout(() => {
        resolve('p1')
    },1000)
});
let p2 = new Promise((resolve,reject) => {
    setTimeout(() => {
        reject('p2')
    },0)
});
Promise.race([p1,p2]).then((value) => {
    console.log(value)
},(err) => {
    console.log(err);		//p2
})
複製代碼

Promise.resolve(value)返回一個成功態,而且將value傳遞給對應的then方法;

Promise.reject(reason)返回一個失敗態,而且將reason傳遞個對應的then方法。

Promise的優缺點

優勢 缺點
解決回調 沒法監測進行狀態
鏈式調用 新創建即執行且沒法取消
減小嵌套 內部錯誤沒法拋出

Promise練習(Node環境)

1.等待狀態

const promise1 = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('success')
    }, 1000)
})
const promise2 = promise1.then(() => {
    return 'an error'
})

console.log('promise1', promise1)
console.log('promise2', promise2)

setTimeout(() => {
    console.log('promise1', promise1)
    console.log('promise2', promise2)
}, 2000)
複製代碼

2.狀態改變

const promise = new Promise((resolve, reject) => {
    resolve('success1')
    reject('error')
    resolve('success2')
})
promise.then((res) => {
	console.log('then: ', res)
})
.catch((err) => {
	console.log('catch: ', err)
})
複製代碼

3.鏈式調用

Promise.resolve(1)
    .then((res) => {
        console.log(res)
        return 2
    })
    .catch((err) => {
        return 3
    })
    .then((res) => {
        console.log(res)
    })
複製代碼

4.鏈式調用

const promise = new Promise((resolve, reject) => {
    setTimeout(() => {
        console.log('executor')
        resolve('success')
    }, 1000)
})

const start = Date.now()
promise.then((res) => {
    console.log(res, Date.now() - start)
})
promise.then((res) => {
    console.log(res, Date.now() - start)
})
//Promise的構造函數只執行一次,而且當即執行;promise的then方法能夠被屢次調用,每次都返回新的promise實例。
複製代碼
const promise = new Promise((resolve, reject) => {
	setTimeout(() => {
        console.log('executor')
        resolve('success')
    }, 1000)
})
const promise2 = promise.then(res => {
    console.log(res);
})
promise2.then(res => {
    console.log(res)
})
複製代碼

5.錯誤捕獲

Promise.resolve()
    .then(() => {
        return new Error('error!!!')
    })
    .then((res) => {
        console.log('then: ', res)
    })
    .catch((err) => {
        console.log('catch: ', err)
    })
複製代碼
Promise.resolve()
    .then(() => {
        throw new Error('error');		//return Promise.reject('error')
    })
    .then((res) => {
        console.log('then: ', res)
    })
    .catch((err) => {
        console.log('catch: ', err)
    })
複製代碼

then或catch方法返回一個錯誤對象並不會被捕獲,只有在拋出錯誤或返回失敗態時纔會引發捕獲。

6.引用錯誤

const promise = Promise.resolve()
  .then(() => {
    return promise
  })
promise.catch(e => {
	console.log(e)
})
//[TypeError: Chaining cycle detected for promise #<Promise>]
複製代碼

返回值不能是promise實例自己,由於此時實例並未構建完成,形成死循環,所以拋出類型錯誤。

7.事件環

process.nextTick(() => {
    console.log('nextTick')
})
Promise.resolve()
    .then(() => {
        console.log('then')
    })
setImmediate(() => {
    console.log('setImmediate')
})
console.log('end')

複製代碼

8.穿透

Promise.resolve(1)
    .then(2)
    .then(Promise.resolve(3))
    .then(console.log)
複製代碼

then和catch方法的參數是一個函數,若非函數則發生穿透

Promise A+規範

規範:promisesaplus.com/

該網站介紹了若須要實現Promise須要實現的規範方法。當多個庫實現本身的Promise類時,爲了能實現相應的效果,該規範提供了實現標準,並提供了promises-aplus-tests測試包測試實現能力。

後續將推出文章介紹如何實現本身的Promise類,敬請期待!

前端優選
相關文章
相關標籤/搜索