Promise 是異步編程的一種解決方案,比傳統的解決方案——回調函數和事件——更合理和更強大。它由社區最先提出和實現,ES6 將其寫進了語言標準,統一了用法,原生提供了Promise對象。 @阮老師編程
promise
的幾種用法:new Promise((resolve, reject) => resolve()).then(() => {
console.log('success');
}, () => {
console.log('err')
})
複製代碼
若是沒有指定reject函數,最後就會執行catch函數
new Promise((resolve, reject) => {
reject('err');
})
.then()
.catch(e => {
console.log(e); // => Error: err
})
複製代碼
Promise.resolve('hahaha').then(data => {
console.log(data); // => 'hahaha'
})
複製代碼
Promise.reject('hahaha').then(data => {
console.log(data); // => 'hahaha'
})
複製代碼
let promise1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('promise1');
}, 1500);
})
let promise2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('promise2');
}, 2000);
})
Promise.all([promise1, promise2]).then(data => {
console.log(data); // => ["promise1", "promise2"]
})
複製代碼
咱們先來簡單的用一下promise
:數組
console.log(1)
new Promise(() => {
console.log(2)
});
console.log(3)
複製代碼
由於咱們都知道promise
是異步的,因此按理會依次輸出 1,3,2
而後咱們運行以後發現 它依次輸出的是 1,2,3
當咱們使用then
的時候:promise
console.log(1)
Promise.resolve().then(() => {
console.log(2);
})
console.log(3)
複製代碼
再次運行,發現結果和咱們以前想的是同樣的,會依次輸出 1,3,2
這是由於promise
的callback
是當即執行的,只有then
方法是異步的bash
A promise must be in one of three states: pending, fulfilled, or rejected.異步
pending
的時候
fulfilled
的時候
value
(成功以後的值)rejected
的時候
reason
(失敗的緣由)class Promise {
constructor(executor) {
// 每個promise實例都有本身的三個狀態,咱們用一個變量來保存當前的狀態
this.status = 'pending';
// 用來保存執行成功時的值
this.value;
// 用來保存執行失敗時的緣由
this.reason;
// 用來將當前狀態改變爲成功態的方法
let resolve = val => {
// 只有當前狀態是`pending` 才能夠改變狀態和值
if (this.status === 'pending') {
this.value = val;
this.status = 'fulfilled';
}
}
// 用來將當前狀態改變爲失敗態的方法
let reject = reason => {
// 只有當前狀態是`pending` 才能夠改變狀態和值
if (this.status === 'pending') {
this.reason = reason;
this.status = 'rejected';
}
}
// 執行executor可能會直接拋出異常,咱們用try catch包起來
try {
executor(resolve, reject);
} catch (e) {
// 若是拋出異常,咱們直接將狀態改成失敗態
reject(e);
}
}
}
複製代碼
then
方法一個promise必須提供一個
then
方法去獲取當前的或最終的value or reason異步編程
promise的
then
方法有兩個參數,分別是成功時執行的方法onFulfilled
和失敗時執行的方法onRejected
函數
onFulfilled
和 onRejected
都是可選的參數
onFulfilled
是一個function
promise
的狀態變成fulfilled
以後被調用,將promise的value
(成功以後的值)做爲他的第一個參數fulfilled
以前不能調用它onRejected
是一個function
promise
的狀態變成rejected
以後被調用,將promise的reason
(失敗的緣由)做爲他的第一個參數rejected
以前不能調用它promise.then(onFulfilled, onRejected)
複製代碼
class Promise {
constructor(executor) {...}
then(onFulfilled, onRejected) {
// 若是當前狀態是成功態,咱們就執行成功的回調,並將存起來的成功的值value傳過去
if (this.status === 'fulfilled') {
onFulfilled(this.value);
}
// 若是當前狀態是失敗態,咱們就執行失敗的回調,並將存起來的失敗緣由reason傳過去
if (this.status === 'rejected') {
onRejected(this.reason);
}
}
}
複製代碼
好了,咱們如今已經能夠簡單的測試一下了測試
new Promise((resolve, reject) => {
resolve('完美');
}).then(data => {
console.log(data); // => 完美
})
複製代碼
完美,可是那麼問題來了,若是咱們的 resolve
或 reject
是異步的呢?ui
new Promise((resolve, reject) => {
setTimeout(() => {
resolve('完美');
}, 1000);
}).then(data => {
console.log(data);
}, err => {
console.log(err);
})
複製代碼
運行以後咱們發現什麼都沒有輸出,onFulfilled
和 onRejected
都沒有打印東西,那說明他們都沒有執行,let me think think...this
這是由於若是咱們的 resolve
或 reject
是異步的,當咱們的then
執行的時候,狀態尚未改變,仍是pending
狀態,因此固然什麼都不會執行。咱們能夠先把 onFulfilled
和 onRejected
存起來,等狀態改變的時候依次執行對應的callback。A: 這難道是?B: 沒錯,這就是訂閱發佈模式。下面咱們來改寫一下:
class Promise {
constructor(executor) {
this.status = 'pending';
this.value;
this.reason;
// 用來保存成功時執行的回調函數
this.onSuccessCallback = [];
// 用來保存失敗時執行的回調函數
this.onErrorCallback = [];
let resolve = val => {
if (this.status === 'pending') {
this.value = val;
this.status = 'fulfilled';
// 狀態改變時 依次執行成功的回調
this.onSuccessCallback.forEach(fn => fn());
}
}
let reject = reason => {
if (this.status === 'pending') {
this.reason = reason;
this.status = 'rejected';
// 狀態改變時 依次執行失敗的回調
this.onErrorCallback.forEach(fn => fn());
}
}
// 執行executor可能會直接拋出異常,咱們用try catch包起來
try {
executor(resolve, reject);
} catch (e) {
// 若是拋出異常,咱們直接將狀態改成失敗態
reject(e);
}
}
then(onFulfilled, onRejected) {
if (this.status === 'fulfilled') {
onFulfilled(this.value);
}
if (this.status === 'rejected') {
onRejected(this.reason);
}
if (this.status === 'pending') {
// 將成功回調和失敗回調都存起來,等待狀態改變,再依次執行對應的方法
this.onSuccessCallback.push(() => {
onFulfilled(this.value);
});
this.onErrorCallback.push(() => {
onRejected(this.reason);
});
}
}
}
複製代碼
onFulfilled
和 onRejected
都是異步調用(微任務)
onFulfilled
和 onRejected
必須做爲一個函數被執行
then
方法必須返回一個promise
promise2 = promise1.then(onFulfilled, onRejected)
複製代碼
onFulFilled
or onRejected
返回了一個值x
,運行promise解析程序onFulfilled
or onRejected
拋出了一個異常e
,promise2
的狀態必須是rejected
,並將e
做爲onRejected
的參數onFulfilled
不是一個function
而且promise1
是fulfilled
,promise2
必須也是fulfilled
而且使用和promise1
相同的value
onRejected
不是一個function
而且promise1
是rejected
,promise2
必須也是rejected
而且使用和promise1
相同的reason
咱們用promise的時候常常promise.then().then().then() ...
這種寫法叫鏈式調用,那怎麼樣才能繼續調用then
方法呢,規範規定then
方法必須返回一個promise實例,這樣就能夠實現鏈式調用了
class Promise {
contructor() {...}
then(onFulfilled, onRejected) {
// 若是onFulfilled和onFulfilled 不是一個函數,咱們給一個默認值
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : val => val;
onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err };
// 咱們將全部東西都包到一個promise實例中,最後返回這個實例,這樣就能夠實現鏈式調用
let promise2;
// `onFulfilled` 和 `onRejected`都是異步調用,咱們先用一個定時器實現異步調用
promise2 = new Promise((resolve, reject) => {
if (this.status === 'fulfilled') {
setTimeout(() => {
try {
// 有一個返回值x,運行解析函數resolvePromise
let x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e);
}
}, 0);
}
if (this.status === 'rejected') {
setTimeout(() => {
try {
// 有一個返回值x,運行解析函數resolvePromise
let x = onRejected(this.reason);;
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e);
}
}, 0);
}
if (this.status === 'pending') {
// 將成功回調和失敗回調都存起來,等待狀態改變,再依次執行對應的方法
this.onSuccessCallback.push(() => {
setTimeout(() => {
try {
// 有一個返回值x,運行解析函數resolvePromise
let x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e);
}
}, 0);
});
this.onErrorCallback.push(() => {
setTimeout(() => {
try {
// 有一個返回值x,運行解析函數resolvePromise
let x = onRejected(this.reason);;
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e);
}
}, 0);
});
}
});
return promise2;
}
}
複製代碼
這裏可能就會有疑惑了,若是有一個返回值x
就運行promise解析程序resolvePromise
,這是什麼鬼?
咱們先來看看規範:
promise的解析程序是將
promise2
和x
做爲參數的函數
若是promise2
和x
是一個promise,那麼拋出一個TypeError
若是x
是一個object
或function
then
存儲x.then
x.then
會拋出異常e
,調用promise的reject
,並將e
做爲它的參數then
是一個function
,使用call
把它的this
指向x
,它的第一個參數是resolvePromise
,第二個參數是rejectPromise
:
resolvePromise
被y
值調用的時候,繼續執行解析程序rejectPromise
執行的時候,調用promise的reject
並將將失敗緣由r
做爲它的參數then
不是一個object
or function
,調用promise的resolve
並將x
做爲它的參數若是x
不是一個object
or function
,調用promise的resolve
並將x
做爲它的參數
總結下來就兩點:
若是promise2
和x
相等,就拋出一個TypeError
,咱們先來看一下
let p = new Promise((resolve, reject) => {
// 返回當前promise實例
return p;
});
p.then(data => {
console.log(data);
}, err => {
console.log(err);
});
複製代碼
運行上面代碼,咱們會發現promise拋出了一個異常,他告訴咱們TypeError: Chaining cycle detected for promise
,這是由於p
的成功仍是失敗取決於本身,本身再等待本身的執行結果,因此他既不會成功也不會失敗
onFulFilled
or onRejected
返回了一個值x
,運行promise解析程序resolvePromise
返回值x
有多是一個常量,對象,也有多是一個promise,這個程序的做用就是若是 x
是一個promise,那就將 x
一直解析到常量位置
let p = new Promise((resolve, reject) => {
resolve(new Promise((resolve, reject) => {
resolve(1111);
}))
})
複製代碼
let resolvePromise = (promise2, x, resolve, reject) => {
// 若是promise2和x相等,就拋出一個類型錯誤
if (promise2 === x) {
return reject(new TypeError('錯了'));
}
// 只容許調用一次resolvePromise
let called;
// 若是x不是一個常量繼續解析
if (x !== null && (typeof x === 'function' || typeof x === 'object')) {
// 調用x.then的時候可能會報錯,由於咱們有可能和別人的Promise庫混用
try {
let then = x.then;
// 若是then是一個函數,證實x是一個promise,繼續解析
if (typeof then === 'function') {
then.call(x, y => {
if (called) {
return;
} else {
called = true;
}
resolvePromise(promise2, y, resolve, reject);
}, r => {
if (called) {
return;
} else {
called = true;
}
reject(r);
})
} else {
// 說明x多是一個普通對象,不是一個promise
resolve(x);
}
} catch (e) {
if (called) {
return;
} else {
called = true;
}
reject(e);
}
} else {
// 說明x是一個常量,直接執行resolve
resolve(x);
}
}
複製代碼
接下來咱們來實現Promise.resolve
Promise.reject
Promise.all
class Promise {
contructor {...}
then() {...}
// 其實Promise.reject和Promise.reject很是簡單
static resolve(value) {
return new Promise((resolve) => {
resolve(value);
})
}
static reject(reason) {
return new Promise((resolve, reject) => {
reject(reason);
})
}
// all方法
static all(promises) {
return new Promise((resolve, reject) => {
// 用來保存結果
let arr = [];
let index = 0;
const saveData = (i, data) => {
arr[i] = data;
if (++index === promises.length) {
resolve(arr);
}
}
for (let i = 0; i < promises.length; i++) {
promises[i].then(data => {
saveData(i, data);
}, reject)
}
})
}
}
複製代碼
好了,接下來咱們來一個完整版的promise
class Promise {
contructor() {
this.status = 'pending';
this.value;
this.reason;
// 用來保存成功時執行的回調函數
this.onSuccessCallback = [];
// 用來保存失敗時執行的回調函數
this.onErrorCallback = [];
let resolve = val => {
if (this.status === 'pending') {
this.value = val;
this.status = 'fulfilled';
// 狀態改變時 依次執行成功的回調
this.onSuccessCallback.forEach(fn => fn());
}
}
let reject = reason => {
if (this.status === 'pending') {
this.reason = reason;
this.status = 'rejected';
// 狀態改變時 依次執行失敗的回調
this.onErrorCallback.forEach(fn => fn());
}
}
// 執行executor可能會直接拋出異常,咱們用try catch包起來
try {
executor(resolve, reject);
} catch (e) {
// 若是拋出異常,咱們直接將狀態改成失敗態
reject(e);
}
}
static resolve(value) {
return new Promise((resolve) => {
resolve(value);
})
}
static reject(reason) {
return new Promise((resolve, reject) => {
reject(reason);
})
}
static all(promises) {
return new Promise((resolve, reject) => {
// 用來保存結果
let arr = [];
let index = 0;
const saveData = (i, data) => {
arr[i] = data;
if (++index === promises.length) {
resolve(arr);
}
}
for (let i = 0; i < promises.length; i++) {
promises[i].then(data => {
saveData(i, data);
}, reject)
}
})
}
then(onFulfilled, onRejected) {
// 若是onFulfilled和onFulfilled 不是一個函數,咱們給一個默認值
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : val => val;
onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err };
// 咱們將全部東西都包到一個promise實例中,最後返回這個實例,這樣就能夠實現鏈式調用
let promise2;
// `onFulfilled` 和 `onRejected`都是異步調用,咱們先用一個定時器實現異步調用
promise2 = new Promise((resolve, reject) => {
if (this.status === 'fulfilled') {
setTimeout(() => {
try {
// 有一個返回值x,運行解析函數resolvePromise
let x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e);
}
}, 0);
}
if (this.status === 'rejected') {
setTimeout(() => {
try {
// 有一個返回值x,運行解析函數resolvePromise
let x = onRejected(this.reason);;
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e);
}
}, 0);
}
if (this.status === 'pending') {
// 將成功回調和失敗回調都存起來,等待狀態改變,再依次執行對應的方法
this.onSuccessCallback.push(() => {
setTimeout(() => {
try {
// 有一個返回值x,運行解析函數resolvePromise
let x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e);
}
}, 0);
});
this.onErrorCallback.push(() => {
setTimeout(() => {
try {
// 有一個返回值x,運行解析函數resolvePromise
let x = onRejected(this.reason);;
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e);
}
}, 0);
});
}
});
return promise2;
}
}
let resolvePromise = (promise2, x, resolve, reject) => {
// 若是promise2和x相等,就拋出一個類型錯誤
if (promise2 === x) {
return reject(new TypeError('錯了'));
}
// 只容許調用一次resolvePromise
let called;
// 若是x不是一個常量繼續解析
if (x !== null && (typeof x === 'function' || typeof x === 'object')) {
// 調用x.then的時候可能會報錯,由於咱們有可能和別人的Promise庫混用
try {
let then = x.then;
// 若是then是一個函數,證實x是一個promise,繼續解析
if (typeof then === 'function') {
then.call(x, y => {
if (called) {
return;
} else {
called = true;
}
resolvePromise(promise2, y, resolve, reject);
}, r => {
if (called) {
return;
} else {
called = true;
}
reject(r);
})
} else {
// 說明x多是一個普通對象,不是一個promise
resolve(x);
}
} catch (e) {
if (called) {
return;
} else {
called = true;
}
reject(e);
}
} else {
// 說明x是一個常量,直接執行resolve
resolve(x);
}
}
複製代碼