在javascript的世界中,全部代碼都是單線程執行的。因爲這個「缺陷」,致使JavaScript的全部網絡操做,瀏覽器事件,都必須是異步執行。 最開始咱們能夠用回調函數來解決這個問題,javascript
function callBack(){
console.log('回調')
}
setTimeout(callBack, 1000)
// 回調
複製代碼
可是隨着業務的不斷深刻,不免會像陷入回調地獄這樣的問題。直到後來咱們有了Promise來解決這個問題。html
promise的基本用法以下: 在實例化一個Promise時,傳入一個函數做爲參數,該函數接受兩個參數,分別爲resolve,reject.如解決則會打印數據,如被拒絕則會打印拒絕緣由java
let p1 = new Promise(function (resolve, reject) {
})
p1.then(function (data) {
console.log(data)
}, function (err) {
console.log(err)
})
複製代碼
3.狀態機制切換 如圖所示,狀態只能由pengding-->fulfilled,或者由pending-->rejected這樣轉變。 只要這兩種狀況發生,狀態就凝固了,不會再變了,會一直保持這個結果。就算改變已經發生了,你再對Promise對象添加回調函數,也會當即獲得這個結果。這與事件(Event)徹底不一樣,事件的特色是,若是你錯過了它,再去監聽,是得不到結果的 數組
function Promise(executor) {
this.state = 'pending'; //狀態
this.value = undefined; //成功結果
this.reason = undefined; //失敗緣由
function resolve(value) { }
function reject(reason) { }
executor(resolve, reject) //當即執行
}
複製代碼
接收一個executor函數,executor函數傳入就執行(當咱們示例化一個promise時,executor當即執行),執行完同步或異步操做後,調用它的兩個參數resolve和reject。其中state保存了promise的狀態,包含三個狀態:等待態(pending)成功態(resolved)和失敗態(rejected)。promise執行成功後的結果由value保存,失敗後的緣由由reason保存。promise
function Promise(executor) {
this.state = 'pending'; //狀態
this.value = undefined; //成功結果
this.reason = undefined; //失敗緣由
resolve = (value) => {
// state改變,resolve調用就會失敗
if (this.state === 'pending') {
// resolve調用後,state轉化爲成功態
this.state = 'fulfilled';
// 儲存成功的值
this.value = value;
}
}
reject = (reason) => {
// state改變,reject調用就會失敗
if (this.state === 'pending') {
// reject調用後,state轉化爲失敗態
this.state = 'rejected';
// 儲存失敗的緣由
this.reason = reason;
}
}
//若是executor執行報錯,直接執行reject
try {
executor(resolve, reject)
} catch (err) {
reject(err) // executor出錯就直接調用
}
}
複製代碼
每個Promise實例都有一個then方法,接收兩個爲函數的參數,它用來處理異步返回的結果,它是定義在原型上的方法。瀏覽器
Promise.prototype.then = function (onFulfilled, onRejected) {
};
複製代碼
當promise的狀態發生了變化,不論成功或失敗都會調用then方法,所以then方法裏面也會根據不一樣的狀態來判斷調用哪個回調函數。 兩個參數的注意事項:網絡
Promise.prototype.then = function (onFulfilled, onRejected) {
if (this.state === 'fulfilled') {
//判斷參數類型,是函數執行之,若是 onFulfilled 不是函數,其必須被忽略
if (typeof onFulfilled === 'function') {
onFulfilled(this.value); // 傳入成功的值
}
}
// 若是 onRejected 不是函數,其必須被忽略
if (this.state === 'rejected') {
if (typeof onRejected === 'function') {
onRejected(this.reason); // 傳入失敗的緣由
}
}
};
複製代碼
上述把promise的基本功能都實現了,可是仍是會存在一個問題,就是promise不支持異步代碼,當resolve或reject在setTimeout中實現時,調用then方法時,此時狀態仍然是pengding,then方法即沒有調用onFulfilled也沒有調用onRejected,也就運行沒有任何結果。異步
咱們能夠參照發佈訂閱模式,在執行then方法時狀態仍是狀態仍是pengding時,把回調函數存儲在一個數組中,當狀態發生改變時依次從數組中取出執行就行了,首先在類上新增兩個Array類型的數組,用於存放回調函數。函數
function Promise(executor) {
this.state = 'pending'; //狀態
this.value = undefined; //成功結果
this.reason = undefined; //失敗緣由
this.onFulfilledFunc = [];//保存成功回調
this.onRejectedFunc = [];//保存失敗回調
function resolve(value) {
// ....
}
function reject(reason) {
// ....
}
executor(resolve, reject) //當即執行
}
複製代碼
並修改then方法post
Promise.prototype.then = function (onFulfilled, onRejected) {
if (this.state === 'pending') {
if (typeof onFulfilled === 'function') {
this.onFulfilledFunc.push(onFulfilled);//保存回調
}
if (typeof onRejected === 'function') {
this.onRejectedFunc.push(onRejected);//保存回調
}
}
if (this.state === 'fulfilled') {
//判斷參數類型,是函數執行之,若是 onFulfilled 不是函數,其必須被忽略
if (typeof onFulfilled === 'function') {
onFulfilled(this.value); // 傳入成功的值
}
}
// 若是 onRejected 不是函數,其必須被忽略
if (this.state === 'rejected') {
if (typeof onRejected === 'function') {
onRejected(this.reason); // 傳入失敗的緣由
}
}
};
複製代碼
修改resolve和reject方法:
function Promise(executor) {
// 其餘代碼
function resolve(value) {
// state改變,resolve調用就會失敗
if (this.state === 'pending') {
// resolve調用後,state轉化爲成功態
this.state = 'fulfilled';
// 儲存成功的值
this.value = value;
this.onFulfilledFunc.forEach(fn => fn(value))
}
}
function reject(reason) {
// state改變,reject調用就會失敗
if (this.state === 'pending') {
// reject調用後,state轉化爲失敗態
this.state = 'rejected';
// 儲存失敗的緣由
this.reason = reason;
this.onRejectedFunc.forEach(fn => fn(reason))
}
}
// 其餘代碼
}
複製代碼
到這裏Promise已經支持了異步操做了。
光是實現了異步操做可不行,咱們經常用到new Promise().then().then()這樣的鏈式調用來解決回調地獄。 規範如何定義then方法:
Promise.prototype.then = function (onFulfilled, onRejected) {
let promise2 = new Promise((resolve, reject) => {
// 其餘代碼
}
return promise2;
};
複製代碼
接下來就處理根據上一個then方法的返回值來生成新Promise對象.
/** * 解析then返回值與新Promise對象 * @param {Object} promise2 新的Promise對象 * @param {*} x 上一個then的返回值 * @param {Function} resolve promise2的resolve * @param {Function} reject promise2的reject */
function resolvePromise(promise2, x, resolve, reject) {
//...
}
複製代碼
當then的返回值與新生成的Promise對象爲同一個(引用地址相同),狀態永遠爲等待態(pending),再也沒法成爲resolved或是rejected,程序會死掉,則會拋出TypeError錯誤
let promise2 = p.then(data => {
return promise2;
});
// TypeError: Chaining cycle detected for promise #<Promise>
複製代碼
所以須要判斷x。
小提示: 爲何取對象上的屬性有報錯的可能?Promise有不少實現(bluebird,Q等),Promises/A+只是一個規範,你們都按此規範來實現Promise纔有可能通用,所以全部出錯的可能都要考慮到,假設另外一我的實現的Promise對象使用Object.defineProperty()惡意的在取值時拋錯,咱們能夠防止代碼出現Bug resolvePromise實現
function resolvePromise(promise2, x, resolve, reject) {
if (promise2 === x) { // 1.x不能等於promise2
reject(new TypeError('Promise發生了循環引用'));
}
let called;
if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
// 2. 多是個對象或是函數
try {
let then = x.then;// 3.取出then方法引用
if (typeof then === 'function') { // 此時認爲then是一個Promise對象
//then是function,那麼執行Promise
then.call(x, (y) => { // 5.使用x做爲this來調用then方法,即then裏面的this指向x
if (called) return;
called = true;
// 6.遞歸調用,傳入y如果Promise對象,繼續循環
resolvePromise(promise2, y, resolve, reject);
}, (r) => {
if (called) return;
called = true;
reject(r);
});
} else {
resolve(x);
}
} catch (e) {
// 也屬於失敗
if (called) return;
called = true;
reject(e); // 4.取then報錯,直接reject
}
} else {
//不然是個普通值
resolve(x);
}
}
複製代碼
此時鏈式調用支持已經實現,在相應的地方調用resolvePromise方法便可。
規範還對onFulfilled和onRejected有規定
完善then方法
Promise.prototype.then = function (onFulfilled, onRejected) {
let promise2 = new Promise((resolve, reject) => {
// onFulfilled若是不是函數,就忽略onFulfilled,直接返回value
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
// onRejected若是不是函數,就忽略onRejected,直接扔出錯誤
onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err };
if (this.state === 'pending') {
this.onFulfilledFunc.push(() => {
// 異步
setTimeout(() => {
try {
let x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
})
this.onRejectedFunc.push(() => {
// 異步
setTimeout(() => {
try {
let x = onRejected(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
})
}
if (this.state === 'fulfilled') {
// 異步
setTimeout(() => {
try {
let x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
}
if (this.state === 'rejected') {
// 異步
setTimeout(() => {
// 若是報錯
try {
let x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
}
})
return promise2;
};
複製代碼
到這裏手寫一個Promise已經所有實現了 完整代碼
function Promise(executor) {
this.state = 'pending'; //狀態
this.value = undefined; //成功結果
this.reason = undefined; //失敗緣由
this.onFulfilledFunc = [];//保存成功回調
this.onRejectedFunc = [];//保存失敗回調
resolve = (value) => {
// state改變,resolve調用就會失敗
if (this.state === 'pending') {
// resolve調用後,state轉化爲成功態
this.state = 'fulfilled';
// 儲存成功的值
this.value = value;
this.onFulfilledFunc.forEach(fn => fn(value))
}
}
reject = (reason) => {
// state改變,reject調用就會失敗
if (this.state === 'pending') {
// reject調用後,state轉化爲失敗態
this.state = 'rejected';
// 儲存失敗的緣由
this.reason = reason;
this.onRejectedFunc.forEach(fn => fn(reason))
}
}
//若是executor執行報錯,直接執行reject
try {
executor(resolve, reject)
} catch (err) {
reject(err) // executor出錯就直接調用
}
}
Promise.prototype.then = function (onFulfilled, onRejected) {
let promise2 = new Promise((resolve, reject) => {
// onFulfilled若是不是函數,就忽略onFulfilled,直接返回value
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
// onRejected若是不是函數,就忽略onRejected,直接扔出錯誤
onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err };
if (this.state === 'pending') {
this.onFulfilledFunc.push(() => {
// 異步
setTimeout(() => {
try {
let x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
})
this.onRejectedFunc.push(() => {
// 異步
setTimeout(() => {
try {
let x = onRejected(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
})
}
if (this.state === 'fulfilled') {
// 異步
setTimeout(() => {
try {
let x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
}
if (this.state === 'rejected') {
// 異步
setTimeout(() => {
// 若是報錯
try {
let x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
}
})
return promise2;
};
function resolvePromise(promise2, x, resolve, reject) {
if (promise2 === x) {
reject(new TypeError('Promise發生了循環引用'));
}
let called;
if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
//多是個對象或是函數
try {
let then = x.then;//取出then方法引用
if (typeof then === 'function') { // 認爲then是一個Promise對象
//then是function,那麼執行Promise
then.call(x, (y) => {
// 成功和失敗只能調用一個
if (called) return;
called = true;
//遞歸調用,傳入y如果Promise對象,繼續循環
resolvePromise(promise2, y, resolve, reject);
}, (r) => {
// 成功和失敗只能調用一個
if (called) return;
called = true;
reject(r);
});
} else {
resolve(x);
}
} catch (e) {
// 也屬於失敗
if (called) return;
called = true;
reject(e);
}
} else {
//不然是個普通值
resolve(x);
}
}
複製代碼
可是隻用構造函數實現固然是不夠的,咱們再用class來實現一個Promise,基本原理同上 class實現
class Promise {
constructor(executor) {
this.state = 'pending';
this.value = undefined;
this.reason = undefined;
this.onResolvedCallbacks = [];
this.onRejectedCallbacks = [];
let resolve = value => {
if (this.state === 'pending') {
this.state = 'fulfilled';
this.value = value;
this.onResolvedCallbacks.forEach(fn => fn());
}
};
let reject = reason => {
if (this.state === 'pending') {
this.state = 'rejected';
this.reason = reason;
this.onRejectedCallbacks.forEach(fn => fn());
}
};
try {
executor(resolve, reject);
} catch (err) {
reject(err);
}
}
then(onFulfilled, onRejected) {
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err };
let promise2 = new Promise((resolve, reject) => {
if (this.state === 'fulfilled') {
setTimeout(() => {
try {
let x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
};
if (this.state === 'rejected') {
setTimeout(() => {
try {
let x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
};
if (this.state === 'pending') {
this.onResolvedCallbacks.push(() => {
setTimeout(() => {
try {
let x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
});
this.onRejectedCallbacks.push(() => {
setTimeout(() => {
try {
let x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0)
});
};
});
return promise2;
}
catch(fn) {
return this.then(null, fn);
}
}
function resolvePromise(promise2, x, resolve, reject) {
if (x === promise2) {
return reject(new TypeError('Chaining cycle detected for promise'));
}
let called;
if (x != null && (typeof x === 'object' || typeof x === 'function')) {
try {
let then = x.then;
if (typeof then === 'function') {
then.call(x, y => {
if (called) return;
called = true;
resolvePromise(promise2, y, resolve, reject);
}, err => {
if (called) return;
called = true;
reject(err);
})
} else {
resolve(x);
}
} catch (e) {
if (called) return;
called = true;
reject(e);
}
} else {
resolve(x);
}
}
//resolve方法
Promise.resolve = function (val) {
return new Promise((resolve, reject) => {
resolve(val)
});
}
//reject方法
Promise.reject = function (val) {
return new Promise((resolve, reject) => {
reject(val)
});
}
//race方法
Promise.race = function (promises) {
return new Promise((resolve, reject) => {
for (let i = 0; i < promises.length; i++) {
promises[i].then(resolve, reject)
};
})
}
//all方法(獲取全部的promise,都執行then,把結果放到數組,一塊兒返回)
Promise.all = function (promises) {
let arr = [];
let i = 0;
function processData(index, data) {
arr[index] = data;
i++;
if (i == promises.length) {
resolve(arr);
};
};
return new Promise((resolve, reject) => {
for (let i = 0; i < promises.length; i++) {
promises[i].then(data => {
processData(i, data);
}, reject);
};
});
}
複製代碼
開源社區提供了一個包用於測試咱們的代碼:promises-aplus-tests,安裝這個包而後運行命令行 promises-aplus-tests [js文件名] 便可驗證。別忘了再代碼後面加上這一段代碼
// 目前是經過他測試 他會測試一個對象
// 語法糖
Promise.defer = Promise.deferred = function () {
let dfd = {}
dfd.promise = new Promise((resolve,reject)=>{
dfd.resolve = resolve;
dfd.reject = reject;
});
return dfd;
}
module.exports = Promise;
複製代碼
參考連接