前言
- Promise 是非同步程式設計的一種解決方案,比傳統的解決方案——回撥函式和事件——更合理和更強大。它由社群最早提出和實現,ES6將其寫進了語言標準,統一了用法,原生提供Promise物件。
- 所謂Promise,簡單說就是一個容器,裡面儲存著某個未來才會結束的事件(通常是一個非同步操作)的結果。從語法上說,Promise 是一個物件,從它可以獲取非同步操作的訊息。Promise 提供統一的 API,各種非同步操作都可以用同樣的方法進行處理。
promise 的特點
promise基本使用
const promise = new Promise(function(resolve, reject) {
// ... some code
if (/* 非同步操作成功 */){
resolve(value);
} else {
reject(error);
}
});
promise.then(function(value) {
// success
}, function(error) {
// failure
});
複製程式碼
術語
- promise 一個帶有then方法的物件或者function
- thenable 定義一個then方法的物件或者function
- exception 是一個使用丟擲語句丟擲的值
- reason promise被拒絕的原因
必備條件
1.promise的狀態
promise必須處在三個狀態之一: pending, fulfilled, or rejected。
複製程式碼
- pending(進行中)
- pending可以轉變成fulfilled或者rejected
- fulfilled(已成功)
- 如果是已成功狀態後,狀態不可再做修改
- 並且有一個不可改變的value值
- reject (已失敗)
- 如果是已失敗狀態後,狀態不可再做修改
- 並且有一個不可改變的reason
程式碼實現如下
function Promise(executor) { // executor是一個執行函式
let self = this;
self.status = 'pending';
self.value = undefined; // 預設成功的值
self.reason = undefined; // 預設失敗的原因
self.onResolvedCallbacks = []; // 存放then成功的回撥
self.onRejectedCallbacks = []; // 存放then失敗的回撥
function resolve(value) { // 成功狀態
if (self.status === 'pending') {
self.status = 'resolved';
self.value = value;
self.onResolvedCallbacks.forEach(function (fn) {
fn();
});
}
}
function reject(reason) { // 失敗狀態
if (self.status === 'pending') {
self.status = 'rejected';
self.reason = reason;
self.onRejectedCallbacks.forEach(function (fn) {
fn();
})
}
}
try {
executor(resolve, reject)
} catch (e) { // 捕獲的時候發生異常,就直接失敗了
reject(e);
}
}
複製程式碼
2.then方法
- promise必須提供一個then方法訪問當前的value或者reason
- promise的then方法接受兩個引數
- promise.then(onFulfilled, onRejected)
- onFulfilled和onRejected是兩個可選引數
- 如果這兩個可選引數不是一個function,則必須被忽略
- 如果onFulfilled是一個function
- 則他必須在promise是fulfiled狀態下被呼叫,並且攜帶promise的value作為第一個引數
- 該方法不能在promise是fulfiled狀態前執行
- 該方法可以被多次呼叫
- 如果onRejected是一個function
- 則他必須在promise是rejected狀態下被呼叫,並且攜帶promise的reason作為第一個引數
- 該方法不能在promise是rejected狀態前被執行
- 該方法可以被多次呼叫
- 在同一個promise中,then方法有可能被多次呼叫
- then方法必須返回一個promise
3.promise的處理過程
- promis的處理過程是我們通過[[Resolve]](promise, x)傳入 一個promise或者一個value的一個抽象的處理過程
- 如果x是一個thenable,它試圖使承諾採用x的狀態,假設x表現至少有點像一個promise。否則,它履行promise與x的值。
- 只要他們公開promiseA+-相容的方法,那麼就可以處理thenables允許promiseA+實現互動操作。
程式碼實現如下:
function resolvePromise(promise2, x, resolve, reject) {
// 有可能這裡返回的x是別人的promise
// 儘可能允許其他亂寫
if (promise2 === x) { //這裡應該報一個型別錯誤,有問題
return reject(new TypeError('迴圈引用了'))
}
// 看x是不是一個promise,promise應該是一個物件
let called; // 表示是否呼叫過成功或者失敗
if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
// 可能是promise {},看這個物件中是否有then方法,如果有then我就認為他是promise了
try { // {then:1}
let then = x.then;
if (typeof then === 'function') {
// 成功
then.call(x, function (y) {
if (called) return
called = true
// y可能還是一個promise,在去解析直到返回的是一個普通值
resolvePromise(promise2, y, resolve, reject)
}, function (err) { //失敗
if (called) return
called = true
reject(err);
})
} else {
resolve(x)
}
} catch (e) {
if (called) return
called = true;
reject(e);
}
} else { // 說明是一個普通值1
resolve(x); // 表示成功了
}
}
Promise.prototype.then = function (onFulfilled, onRjected) {
//成功和失敗預設不穿給一個函式
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : function (value) {
return value;
}
onRjected = typeof onRjected === 'function' ? onRjected : function (err) {
throw err;
}
let self = this;
let promise2; //返回的promise
if (self.status === 'resolved') {
promise2 = new Promise(function (resolve, reject) {
// 當成功或者失敗執行時有異常那麼返回的promise應該處於失敗狀態
// x可能是一個promise 也有可能是一個普通的值
setTimeout(function () {
try {
let x = onFulfilled(self.value);
// x可能是別人promise,寫一個方法統一處理
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
})
})
}
if (self.status === 'rejected') {
promise2 = new Promise(function (resolve, reject) {
setTimeout(function () {
try {
let x = onRjected(self.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
})
})
}
// 當呼叫then時可能沒成功 也沒失敗
if (self.status === 'pending') {
promise2 = new Promise(function (resolve, reject) {
// 此時沒有resolve 也沒有reject
self.onResolvedCallbacks.push(function () {
setTimeout(function () {
try {
let x = onFulfilled(self.value);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e)
}
})
});
self.onRejectedCallbacks.push(function () {
setTimeout(function () {
try {
let x = onRjected(self.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
})
});
})
}
return promise2;
}
複製程式碼