要判斷一個 JavaScript 變量是否為 promise,1. 首先檢查其是否具有 then 方法,即非空且為對象,并且 obj.then 是函數;2. 更嚴格的方式是結合原生 promise 檢測與 then 方法檢測,使用 instanceof 判斷是否為原生 promise 或符合 promise/a+ 規范的對象;3. 避免使用 typeof 或 constructor 判斷,因其無法準確識別 promise 類型或受上下文影響導致錯誤;4. 為兼容不同 promise 庫,應僅關注 then 方法的規范性;5. 在 typescript 中可使用類型守衛 ispromise(value) 并返回 value is promise
判斷一個 JavaScript 變量是否為 Promise,核心在于檢測它是否具有 Promise 的必要特征:then 方法。更嚴謹地說,需要考慮變量是否遵循 Promises/A+ 規范。
檢測方案如下:
方案一:基于 then 方法的檢測
function isPromiseLike(obj) { return obj !== null && typeof obj === 'Object' && typeof obj.then === 'function'; } // 示例 const promise = new Promise((resolve, reject) => { setTimeout(() => resolve('Resolved!'), 100); }); const notPromise = { value: 'Not a promise' }; console.log(isPromiseLike(promise)); // true console.log(isPromiseLike(notPromise)); // false
這個方法簡單直接,檢查對象是否非空、類型為 object,并且擁有一個類型為 function 的 then 屬性。 這種方法足夠應對大多數場景。
方案二:更嚴格的 Promise 實例檢測 (兼容性考慮)
function isNativePromise(obj) { return typeof Promise !== 'undefined' && obj instanceof Promise; } function isPromise(obj) { return isNativePromise(obj) || isPromiseLike(obj); } // 示例 const promise = new Promise((resolve, reject) => { setTimeout(() => resolve('Resolved!'), 100); }); const thenable = { then: function(resolve, reject) { resolve('Thenable Resolved'); } }; console.log(isPromise(promise)); // true console.log(isPromise(thenable)); // true, 如果只用 isNativePromise 則會返回 false
這個方案首先檢查瀏覽器是否支持原生的 Promise 對象,如果支持,則使用 instanceof 操作符來判斷。 同時,為了兼容一些 polyfill 或者第三方 Promise 庫,還需要結合 isPromiseLike 函數,確保即使不是原生 Promise 實例,只要具有 then 方法,也能被正確識別。這種方法更全面,考慮到兼容性問題。
為什么不能簡單地使用 typeof 或 constructor 來判斷?
直接使用 typeof 無法區分 Promise 和其他對象,因為 Promise 的類型也是 object。 而 constructor 方法可能被篡改,或者在不同的 JavaScript 上下文中(例如 iframe)指向不同的 Promise 構造函數,導致判斷錯誤。 例如:
// 假設在不同的 iframe 中 const iframe = document.createElement('iframe'); document.body.appendChild(iframe); const iframeWindow = iframe.contentWindow; const promise = new iframeWindow.Promise((resolve, reject) => { resolve('From iframe'); }); console.log(promise.constructor === Promise); // false,因為 Promise 指向的是主窗口的 Promise 構造函數
如何處理不同 Promise 庫之間的兼容性問題?
不同的 Promise 庫(例如 Bluebird, Q)可能對 Promise 的實現細節有所不同。 為了確保兼容性,最佳實踐是使用 isPromiseLike 函數,因為它只關注 Promise 的核心特征:then 方法。 只要對象具有符合規范的 then 方法,就可以被視為 Promise,而無需關心它是由哪個庫創建的。
在 typescript 中如何更安全地判斷 Promise?
在 TypeScript 中,可以使用類型守衛 (Type Guard) 來更安全地判斷 Promise。 類型守衛可以確保在類型檢查階段就排除掉不符合 Promise 類型的變量,從而避免運行時錯誤。
function isPromise(obj: any): obj is Promise<any> { return typeof obj === 'object' && obj !== null && typeof obj.then === 'function'; } function processValue(value: any) { if (isPromise(value)) { // TypeScript 知道 value 是 Promise<any> 類型 value.then(result => console.log('Promise result:', result)); } else { console.log('Not a promise:', value); } }
通過 obj is Promise