在js中,異步是一個很是重要的組成部分,它基於事件循環,保證了優先級更高任務的優先執行權,好比js下載、UI渲染、js中非異步的任務,異步使得單進程的js可以作到非阻塞,這在node顯得攸關重要,它使得js沒必要等待I/O操做返回結果,而能去處理其餘任務。可是異步也存在着缺點,最明顯的就是回地獄,而Pormise規範的出現,讓開發者從回調地獄從解放出來。它由社區提出和實現,以後被es6加入到標準當中。node
Promise最基本的是由status、resolve、onResolved、reject、onRejected、then和catch幾部分組成:webpack
status:狀態管理,有pendding,fulfilled和rejected三種狀態,且狀態一經改變是沒法逆轉的;
resolve: 一個函數,當調用該函數時,說明異步任務執行成功了,promise的status由pendding轉爲fulfilled,且會調用成功的回調函數onResolved;
reject: 一個函數,當調用該函數時,說明異步任務執行失敗,promise的status由pendding轉爲rejected,且會調用失敗的回調函數onRejected;
then: 一個函數,在then的參數裏面,咱們須要定義回調成功須要執行的成功回調函數,promise會將這個成功回調函數註冊到onResolved,由resolve觸發調用,而且還支持鏈式調用;
catch: 一個函數,在reject的參數裏面,咱們須要回調失敗須要執行的失敗回調函數,promise會將這個失敗回調函數註冊到onRejected,由reject觸發調用;es6
備註: 這裏沒講race和all方法等,有興趣的能夠去看下文檔。web
經過new建立一個Promise對象,傳入一個函數參數,這個函數包含resolve和reject參數,這兩個參數,如上所述也是函數,當異步任務完成的時候,調用resolve方法,當異步任務失敗的時候,調用reject方法。eg:面試
var p = new Promise(function(resolve, reject){
setTimeout(function() {
resolve(1)
}, 1000)
})
複製代碼
then
方法,參數也是一個回調函數,當你調用resolve方法後,這個回調函數就會調用,且resolve函數傳入的參數,會帶給這個回調函數的參數,eg:編程
p.then(function(arg){
console.log(arg) // 1
})
複製代碼
catch
方法,參數一樣是一個回調函數,當你調用reject方法後,這個回調函數就會調用,且reject函數傳入的參數,會帶給這個回調函數,使用例子和then同理。
因此咱們的Promise的調用能夠是這樣的:promise
var p = new Promise(function(resolve, reject){
// 異步任務執行,且看結果調用resolve仍是reject
}).then(function(){
// do something1
return new Promise...
}).then(function() {
// do something2
return new Promise....
}).then(function() {
// do something3
return new Promise....
}).then(function() {
// do something4
return new Promise....
})
複製代碼
對比用回調的方式bash
doAsyncTask1(function(){
// do something1
doAsyncTask2(function() {
// do something2
doAsyncTask3(function() {
// do something3
doAsyncTask4(function(){
// do something4
})
})
})
})
複製代碼
是否是比起異步編程,promise更人性化呢?babel
上面promise的調用方式其實還不夠優雅,還有更加優雅的調用方式,那就是async await方式,咱們來看下如何用。異步
async testPromise() {
var result = await new Promise(function(resolve, reject){
setTimeout(function(){
resolve(1)
}, 2000)
})
console.log(result)
var result1 = await new Promise(function(resolve, reject){
setTimeout(function(){
resolve(1)
}, 2000)
})
console.log(result1)
}
複製代碼
是否是已經變成了咱們同步的編程方式了?
這裏有兩點須要注意的:
面試官很喜歡問你如何實現一個Promise,這個時候咱們就須要對Promise有必定的理解,咱們不去看Promise/A+規範,太複雜晦澀難懂了,咱們就按照上面講的Promise的組成來實現一個簡單的Promise。
function _Promise(fn) {
this.status = 'pending';
this.onResolved = null;
this.onRejected = null;
fn.call(this, this.resolve.bind(this), this.reject.bind(this))
}
_Promise.prototype.resolve = function(arg) {
if(this.status === 'pending') {
this.onResolved(arg)
this.status = 'fulfilled'
}
}
_Promise.prototype.reject = function(arg) {
if(this.status === 'pending') {
this.onRejected(arg)
this.status = 'rejected'
}
}
_Promise.prototype.then = function(onResolved) {
this.onResolved = onResolved
}
_Promise.prototype.reject = function(onRejected) {
this.onResolved = onRejected
}
複製代碼
這就是一個最簡單的Promise實現方式,可是它還不支持鏈式調用。因此咱們須要在then方法再返回一個Promise, 咱們改造一下:
function _Promise(fn) {
this.status = 'pending';
this.onResolved = null;
this.onRejected = null;
this.childReject = null;
this.childResolve = null;
fn.call(this, this.resolve.bind(this), this.reject.bind(this))
}
_Promise.prototype.resolve = function(arg) {
if(this.status === 'pending') {
if(this.onResolved) {
var ret = this.onResolved(arg)
// 若是第二個then return的是一個用戶自定義的promise,咱們稱爲PromiseB,則須要把childResolve賦值給這個PromiseB的onResolved,交給這個PromiseB來執行
if(ret instanceof _Promise) {
ret.onResolved = this.childResolve
return
}
// 不然直接調用childResove確保第二個then的回調執行
this.childResolve && this.childResolve()
}
this.status = 'fulfilled'
}
}
_Promise.prototype.reject = function(arg) {
if(this.status === 'pending') {
this.onRejected && this.onRejected(arg)
this.childReject && this.childReject()
this.status = 'rejected'
}
}
_Promise.prototype.then = function(onResolved) {
this.onResolved = onResolved
var that = this
// 定義一個promise,咱們簡稱PromiseA,以便鏈式調用
return new _Promise(function(resolve, reject){
// 這裏須要確保resolve方法在第一個promise的resolve調用後調用,那麼須要把它保存到第一個promise裏面
that.childResolve = resolve
that.childReject = reject
})
}
_Promise.prototype.reject = function(onRejected) {
this.onResolved = onRejected
}
// 例子
new _Promise((resolve) => {
setTimeout(() => {
resolve()
}, 2000)
}).then(() => {
console.log('promise success')
return new _promise((resolve) => {
setTimeout(() => {
resolve()
}, 1000)
})
}).then(() => {
console.log('promise2 success')
})
複製代碼
這個實現,有三個Promise,第一個是首個Promise,第二個PormiseA即便若是then方法沒有返回用戶自定義的Promise的時候,咱們方便鏈式調用的Promise,第三個PromiseB是then方法返回用戶自定義的Promise的時候,咱們須要把第二個then的回調交還給PromiseB執行。