以前一直對於promise,就是一頓用,歷來沒有深究其內部原理,無心間看到一些關於promise輸出順序的題目也是一頓瞎猜。。。哈哈,在個人同事對promise進行分享以後(雖然我由於起不來沒有聽),可是學習promise的意識就此種下。。。啥也不說了,就是學。。。css
帶着疑問去學習:jquery
- promise是幹什麼的?
- promise有哪些方法?
- pomise如何實現鏈式調用?以前jquery也是能夠鏈式調用,他們以前的區別?
- promise的then方法是如何肯定在前面resolve或者reject執行完拿到結果再執行的?
- 手寫一個知足promiseA+的promise方法(寫了一下,發現沒法經過promiseA+的測試用例,已放棄,拿着別人寫的仔細看了一下)
promise是一種異步編程的解決方案,它能夠實現把異步操做按照同步操做的流程表達出來。它能夠接收一個異步請求,而後在其then方法能夠獲得前面異步請求的結果進行後續操做,使用鏈式調用的方式將異步請求的結果按照同步的方式表達出來。編程
1. promise只是異步編程的解決方案,自身並非異步請求,在new Promise()內部傳的方法會當即執行。數組
const promise = new Promise(function (resolve, reject) {
console.log('12')
setTimeout(function () {
resolve('success')
}, 1000)
})
promise.then(function (success) {
console.log(success)
})
// 執行結果: 先輸出12,過了一秒後再輸入success
複製代碼
2. new Promise內的resolve是異步執行的,且全部then方法和catch方法也會將在當前腳本全部同步任務執行完纔會執行promise
const myPromise = new Promise(function (resolve, reject) {
console.log('1')
resolve('2')
console.log('3')
})
myPromise.then(function (success) {
console.log(success)
})
setTimeout (function () {
// 這裏能夠看到前面myPromise已經resolved了,可是他仍是會在最後執行,由於then內部也是異步操做
myPromise.then(function (success) {
console.log('again', success)
})
console.log('4')
}, 1000)
console.log('5')
// 執行結果是: 1 3 5 2 4 again 2
複製代碼
const promise = new Promise(function(resolve, reject) {
if (/* 異步操做成功 */){
resolve(value);
} else {
reject(error);
}
});
promise.then(function(value) {}, function (error) {})
複製代碼
基本用法簡單歸納一下:bash
then()和catch()默認返回的都是一個Promise實例對象,即便return了別的信息,返回的仍然是Promise實例對象,仍是且這個promise的狀態都是resolve異步
無論Promise對象最後狀態如何,finally方法都會執行,(promise是pedding狀態不會執行finally方法,resolve和reject都會執行)ide
將現有對象轉換爲Promise對象異步編程
將多個Promise實例包裝成一個新的Promise實例。函數
var p1 = new Promise((resolve) => {
setTimeout(() => {
resolve('p1')
}, 1000)
})
var p2 = new Promise((resolve) => {
setTimeout(() => {
resolve('p2')
}, 2000)
})
var p3 = new Promise((resolve) => {
setTimeout(() => {
resolve('p3')
}, 3000)
})
var p = Promise.all([p1, p2, p3]).then((value) => {
console.log(value)
}).catch((err) => {
console.log(err)
})
// 返回值:['p1', 'p2', 'p3']
複製代碼
這裏Promise的狀態是由p1,p2,p3共同決定的:
將現有對象轉換爲Promise對象,只要p一、p二、p3之中有一個實例率先改變狀態,p的狀態就跟着改變。那個率先改變的 Promise 實例的返回值,就傳遞給p的回調函數。
在每一次執行Promise的then或者catch方法後,返回的都是一個新的Promise實例化對象,使得又一次可使用then或者catch方法,而且能夠拿到上一次then方法return回來的值。
例如:
then內部的函數return了一個數值a。咱們發現then方法執行完,返回的是一個新的promise實例,其promisevalue爲a
根據以上鋪墊,咱們就知道,其鏈式調用就是經過then和catch方法內部return new Promise()
經過return this,在其原型方法執行結束都會teturn其自己
var Jquery = function () {
}
Jquery.prototype = {
css: function (attr, data) {
return this
},
hide: function () {
console.log('hide')
return this
}
}
var jq = new Jquery()
jq.css().hide()
複製代碼
這裏jq調用了css方法,首先會在jq上找css方法,沒有找到,而後就去其構造函數Jquery的原型中找,找到css方法並執行,在原型css方法裏return this中的this指向的是jq
promise執行了then或者catch方法後,每一次返回的是一個新的Promise實例,jquey一直返回的都是其自己
這裏學習了一下別人寫的promise,經過這個學習到了一些,中間關於這部分的代碼
const PENDING = 'pending'
const RESOLVED = 'resolved'
const REJECTED = 'rejected'
// promise 對象上有resolve,reject,all,race等方法,原型上有then,catch,finally方法
class myPromise {
constructor (fn) {
// 首先須要判斷fn是不是一個函數
if (typeof fn != 'function') return 'fn is not function'
this.PromiseStatus = PENDING
this.PromiseValue = null
this.PromiseReason = null
this.onResolvedCallbacks = []
this.onRejectedCallbacks = []
const resolve = value => { // 代碼塊一
if(value instanceof Promise) {
return value.then(resolve, reject);
}
// new Promise構造函數傳進來的函數fn會當即執行,可是在new Promise內resolve和reject是異步操做(如何作到異步操做 =>使用setTimeout確保new Promise後的then和catch方法會在fn函數執行完才觸發)
setTimeout(() => {
if (this.PromiseStatus == PENDING) {
this.PromiseStatus = RESOLVED
this.PromiseValue = value
this.onResolvedCallbacks.map(cb => {
this.PromiseValue = cb(this.PromiseValue)
})
}
})
}
const reject = value => {
setTimeout(() => {
if (this.PromiseStatus == PENDING) {
this.PromiseStatus = REJECTED
this.PromiseReason = value
this.onRejectedCallbacks.map(cb => {
this.PromiseReason = cb(this.PromiseReason)
})
}
})
}
try { // 代碼塊二
// 當即執行new Promise內的
fn(resolve, reject)
} catch (err) {
reject(err)
}
}
then (onResolved, onRejected) { // 代碼塊三
onResolved = typeof onResolved === 'function' ? onResolved : function (v) { return v}
onResolved = typeof onRejected === 'function' ? onRejected : function (v) { return v}
console.log('this.PromiseStatus', this.PromiseStatus)
if (this.PromiseStatus === RESOLVED) { // 代碼塊三 條件一
return new myPromise((resolve, reject) => {
// 何時會出現resolve一進來就變成RESOLVED或者REJECTED? 同一個Promise實例被屢次調用其then時,這樣會走下面的邏輯,爲肯定then內部仍是會異步執行,使用setTimeout
setTimeout(() => {
try {
// 執行then內部的方法
let x = onResolved(this.PromiseValue)
resolvePromise(newPromise, x, resolve, reject);
} catch (err) {
reject(err)
}
})
})
}
if (this.PromiseStatus === REJECTED) { // 代碼塊三 條件二
return new myPromise((resolve, reject) => {
setTimeout(() => {
try {
let x = onRejected(this.PromiseReason)
resolvePromise(newPromise, x, resolve, reject);
} catch (err) {
reject(err)
}
})
})
}
// 當沒有觸發resolve或者reject時,將onResolved和onRejected存放在onResolvedCallbacks/onRejectedCallbacks集合中
if (this.PromiseStatus === PENDING) { // 代碼塊三 條件三
return new myPromise ((resolve, reject) => {
this.onResolvedCallbacks.push(value => {
try {
let x = onResolved(value)
resolvePromise(newPromise, x, resolve, reject);
} catch (err) {
reject(err)
}
})
this.onRejectedCallbacks.push(reason => {
try {
let x = onRejected(reason)
resolve(x)
} catch (err) {
reject(err)
}
})
})
}
}
}
複製代碼
根據以上代碼,咱們進行簡單的分析:
new myPromise(function (resolve, reject) {
console.log(1)
resolve(2)
}).then(function (success) {
console.log(success)
})
複製代碼
上面的代碼中,首先構造函數初始化一些值
this.PromiseStatus = PENDING // 初始promise的狀態
this.PromiseValue = null // 初始promise resolve狀態時的value值
this.PromiseReason = null // 初始promise reject狀態時的value值
this.onResolvedCallbacks = [] // 存儲resolved狀態對應的onResolvedCallbacks事件數組
this.onRejectedCallbacks = [] // 存儲rejected狀態對應的onRejectedCallbacks事件數組
複製代碼
例如上面簡單的例子:其經歷的步驟有:
首先進入myPromise構造函數內,進入代碼塊二,當即執行myPromise傳進來的函數fn,這裏PromiseStatus=PENDING
try { // 代碼塊二
// 當即執行new Promise內的
fn(resolve, reject)
} catch (err) {
reject(err)
}
複製代碼
執行fn時,進一步執行resolve函數,進入代碼塊一,這裏首先對resolve傳進來的參數進行了類型判斷,而後咱們能夠看到setTimeout代碼,這裏將setTimeout內部的事件放在eventLoop的最後。這裏PromiseStatus=PENDING
setTimeout(() => {
if (this.PromiseStatus == PENDING) {
this.PromiseStatus = RESOLVED
this.PromiseValue = value
this.onResolvedCallbacks.map(cb => {
this.PromiseValue = cb(this.PromiseValue)
})
}
})
複製代碼
觸發myPromise的then方法,進入到代碼塊三,進行條件判斷,進入到代碼塊三的 條件三,返回一個新的myPromise實例,這裏會當即執行下面代碼
this.onResolvedCallbacks.push(value => {
try {
let x = onResolved(value)
resolvePromise(newPromise, x, resolve, reject);
} catch (err) {
reject(err)
}
})
複製代碼
這個操做就是向onResolvedCallbacks裏push一個函數
上述操做結束後,再執行前面放在eventLoop最後的事件
setTimeout(() => {
if (this.PromiseStatus == PENDING) {
this.PromiseStatus = RESOLVED
this.PromiseValue = value
this.onResolvedCallbacks.map(cb => {
this.PromiseValue = cb(this.PromiseValue)
})
}
})
複製代碼
這裏找到更改了PromiseStatus的狀態,並執行onResolvedCallbacks數組中的函數,這個函數是以前代碼塊三中的
value => {
try {
let x = onResolved(value)
resolvePromise(newPromise, x, resolve, reject);
} catch (err) {
reject(err)
}
}
複製代碼
因此,promise的then方法是如何肯定在前面resolve或者reject執行完拿到結果再執行的,分兩種狀況:
a = new Promise((resolve, reject) => {
resolve(1)
})
複製代碼