Promise 承諾,在異步編程中用來描述異步任務,當前不執行,承諾執行,美劇裏常常是I
promise~ 掛在嘴邊javascript
new Promise(..) 構造器前端
var p = new Promise( function(resolve,reject){ // 接收函數做爲構造器參數 // resolve(..)用於將promise狀態從pending改成fulfilled // reject(..)用於將promise狀態從pending改成rejected } );
resolve(..) 既可能完成 promise,也可能拒絕,要根據傳入參數而定。java
若是傳給 resolve(..) 的是一個非 Promise、非 thenable 的當即值,這個 promise 就會用這個值完成。git
若是傳給 resolve(..) 的是一個真正的 Promise 或 thenable 值,這個值就會被遞歸展開,而且(要構造的)promise 將取用其最終決議值或狀態。es6
Promise.resolve 建立一個已完成的 Promisegithub
語法上二者是等同的面試
var p1 = new Promise( function(resolve,reject){ resolve( "Oops" ); } ); var p2 = Promise.resolve( "Oops" );
Promise.reject 建立一個已被拒絕的 Promise編程
var p1 = new Promise( function(resolve,reject){ reject( "Oops" ); } ); var p2 = Promise.reject( "Oops" );
輸入一組promise,返回一個新的promise。只有傳入的所有 promise 的狀態都變爲fulfilled
,返回 promise 才能改變爲fulfilled
。能夠理解爲邏輯與(&&
)數組
輸入一組promise,返回一個新的promise。只有傳入的全部 promise 的狀態都改變後(fulfilled
或rejected
),返回 promise 才能改變爲fulfilledpromise
輸入一組promise,返回一個新的promise。結果的promise與第一個 狀態發生改變(fulfilled
或rejected
)promise相同
接受一組 Promise 實例做爲參數,包裝成一個新的 Promise 實例。只要參數實例有一個變成fulfilled
狀態,包裝實例就會變成fulfilled
狀態;若是全部參數實例都變成rejected
狀態,包裝實例就會變成rejected
狀態。相似於邏輯或 (||
)
若向 Promise.all([ .. ]) 傳入空數組,它會當即完成,但 Promise.race([ .. ]) 會掛住,且永遠不會完成。
實例方法:
then(..) 接受一個或兩個參數:第一個用於完成回調,第二個用於拒絕回調。若是二者中
的任何一個被省略或者做爲非函數值傳入的話,就會替換爲相應的默認回調。默認完成回
調只是把消息傳遞下去,而默認拒絕回調則只是從新拋出(傳播)其接收到的出錯緣由
只接受一個拒絕回調做爲參數,並自動替換默認完成回調。換句話說,它等價於 then(null,..):
接收一個參數,不論promise的最終狀態如何都會執行,
then(..) 和 catch(..) 也會建立並返回一個新的 promise,這個 promise 能夠用於實現
Promise 鏈式流程控制。
若是完成或拒絕回調中拋出異常,返回的 promise 是被拒絕的。若是任意一個回調返回非 Promise、非 thenable 的當即值,這個值會被用做返回 promise 的完成值。
若是完成處理函數返回一個 promise 或 thenable,那麼這個值會被展開,並做爲返回promise 的決議值。
對於錯誤處理,通常咱們使用try…catch,可是它只能捕捉同步代碼的異常。
try{ setTimeout(function () { console.log('happen',x) }) }catch (e) { console.log('exception',e) } // Uncaught ReferenceError: x is not defined
在promise中,若是發生異常,將由then()中的reject處理函數進行錯誤捕獲。
Promise.reject(10).then((val)=>{ console.log(val) },(err)=>{ console.log('then',err) }) // then 10
Promise.resolve(10).then(function (val) { console.log(val) return Promise.reject(val) }).then(val => { console.log(val) }, err => { console.log('then', err) }) // 10 // then 10
若是忘記在then()中對異常進行捕獲,將會丟失被忽略和拋棄的Promise錯誤。因而,實現Promise異常控制的最佳實踐就是以catch()結尾來進行異常處理。
Promise.resolve(10).then(function (val) { console.log(val) return Promise.reject(val) }).then(val => { console.log(val) }).catch(err=>{ console.log('err',err) }) // err 10
可是,若是咱們在catch中發生了異常呢?
Promise.reject(10).catch(err=>{ console.log('catch',10+x) }) // ReferenceError: x is not defined Promise.reject(10).catch(err => { console.log('catch', 10 + x) }).catch(err=>{ console.log('catch2',err) }) // catch2 ReferenceError: x is not defined
其實咱們能夠在調用鏈後面再次補充一個catch來保證捕獲任何可能出現的錯誤。可是這樣的寫法不是很優雅,有種經常使用的方法是使用done(標誌着promise的結尾,不會建立和返回promise,不在es6規範中),現階段能夠經過增長 polyfill
Promise.prototype.done = function (onFulfilled, onRejected) { this.then(onFulfilled, onRejected) .catch(function (err) { // 拋出一個全局錯誤 setTimeout(() => { throw err }, 0); }); };
在瀏覽器中,咱們可使用 unhandledrejection
事件來捕獲這類 error:
window.addEventListener('unhandledrejection', function(event) { // 這個事件對象有兩個特殊的屬性: alert(event.promise); // [object Promise] - 生成該全局 error 的 promise alert(event.reason); // Error: Whoops! - 未處理的 error 對象 }); new Promise(function() { throw new Error("Whoops!"); });
若是出現了一個 error,而且在這兒沒有 .catch
,那麼 unhandledrejection
處理程序(handler)就會被觸發,並獲取具備 error 相關信息的 event
對象,因此咱們就能作一些後續處理了
如下代碼的執行順序是?
const promise=new Promise((resolve,reject)=>{ console.log(1); resolve(); console.log(2); }) promise.then(()=>{ console.log(3); }) console.log(4); // 1 // 2 // 4 // 3
new Promise
是同步執行的,因此先打印1,2console.log(4)
,接着打印 4console.log(3)
,打印出3第二段代碼
const promise=Promise.resolve(1).then(2).then(Promise.resolve(3)).then(console.log);//1
過程分解
- Promise.resolve(1) 生成的promise ,返回值爲1,狀態爲fulfilled
- then(2)中,傳入了非函數,返回的promise 的值爲上一步的promise的值(即爲1),狀態爲fulfilled
- then(Promise.resolve(3)) 這一步中參數類型爲promise,仍取上一步的promise的值(即爲1),狀態爲fulfilled
- then(console.log), 這一步參數爲函數,傳入值1
class Promise { constructor (executor) { // 參數校檢 if (typeof executor !== 'function') { throw new TypeError(`Promise resolver ${executor} is not a function`) } this.initValue() this.initBind() try { executor(this.resolve, this.reject) } catch (e) { this.reject(e) } } initValue () { // state:pending/fulfilled/rejected this.value = null//終值 this.reason = null//拒因 this.state = Promise.PENDING //用於處理異步 this.onFulfilledCallbacks = [] //成功回調 this.onRejectedCallbacks = []//失敗回調 } resolve (value) { //成功後的一系列操做(狀態的改變,成功回調的執行) //狀態不可逆 從pending開始 if (this.state === Promise.PENDING) { this.state = Promise.FULFILLED this.value = value this.onFulfilledCallbacks.forEach(fn => fn(this.value)) } } //綁定this initBind () { this.resolve = this.resolve.bind(this) this.reject = this.reject.bind(this) } reject (reason) { //失敗後的一系列操做(狀態的改變,失敗回調的執行) if (this.state === Promise.PENDING) { this.state = Promise.REJECTED this.reason = reason this.onRejectedCallbacks.forEach(fn => fn(this.reason)) } } catch (onRejected) { return this.then(null, onRejected) // 至關於then裏的成功回調只傳個null } then (onFulfilled, onRejected) { //實現鏈式調用且改變了後面then方法的值,必須經過新的實例 let promise2 = new Promise((resolve, reject) => { // 參數校檢 if (typeof onFulfilled !== 'function') { onFulfilled = function (value) { return value } } if (typeof onRejected !== 'function') { onRejected = function (reason) { throw reason } } if (this.state === Promise.FULFILLED) { //模擬異步任務 setTimeout(() => { try { const x = onFulfilled(this.value) Promise.resolvePromise(promise2, x, resolve, reject) } catch (e) { reject(e) } }) } if (this.state === Promise.REJECTED) { setTimeout(() => { try { const x = onRejected(this.reason) Promise.resolvePromise(promise2, x, resolve, reject) } catch (e) { reject(e) } }) } // 處理異步 if (this.state === Promise.PENDING) { this.onFulfilledCallbacks.push((value) => { setTimeout(() => { try { const x = onFulfilled(value) Promise.resolvePromise(promise2, x, resolve, reject) } catch (e) { reject(e) } }) }) this.onRejectedCallbacks.push(reason => { setTimeout(() => { try { const x = onRejected(reason) Promise.resolvePromise(promise2, x, resolve, reject) } catch (e) { reject(e) } }) }) } }) return promise2 } } Promise.resolvePromise = function (promise, x, resolve, reject) { // x與promise相等時 循環引用 if (promise === x) { return reject(new TypeError('Chaining cycle detected for promise')) } // 函數調用一次的開關項 防止重複調用 let called if (x instanceof Promise) { /** * x爲 Promise * 1. x 爲 pending,promise需保持 pending 直至x被拒絕或執行 * 2. x 爲 fulfilled 用相同的值執行promise * 3. x 爲 rejected 用相同的拒因執行promise */ x.then(value => {Promise.resolvePromise(promise, value, resolve, reject)}, reason => {reject(reason)}) } else if (x && (typeof x === 'object' || typeof x === 'function')) { // x 爲對象或函數 try { let then = x.then // 處理thenable if (typeof then === 'function') { then.call(x, value => { if (called) return called = true Promise.resolvePromise(promise, value, resolve, reject) }, reason => { if (called) return called = true reject(reason) }) } else { resolve(x) } } catch (e) { if (called) return called = true reject(e) } } else { resolve(x) } } Promise.resolve = function (val) { return new Promise(resolve => { resolve(val) }) } Promise.reject = function (reason) { return new Promise((resolve, reject) => { reject(reason) }) } Promise.race = function (promises) { return new Promise((resolve, reject) => { for (let i = 0; i < promises.length; i++) { promises[i].then(data => { resolve(data) }) } }) } Promise.all = function (promises) { let result = [] return new Promise((resolve, reject) => { for (let i = 0; i < promises.length; i++) { promises[i].then(value => { result.push(value) if (result.length === promises.length) { resolve(arr) } }, reject) } }) } Promise.PENDING = 'pending' Promise.FULFILLED = 'fulfilled' Promise.REJECTED = 'rejected' Promise.defer = Promise.deferred = function () { let dfd = {} dfd.promise = new Promise((resolve, reject) => { dfd.resolve = resolve dfd.reject = reject }) return dfd } module.exports = Promise