Skip to content

isPromise

Checks whether a value is of Promise type.

Basic Usage

typescript
import { isPromise } from 'radash'

console.log(isPromise(Promise.resolve(123)))     // true
console.log(isPromise(new Promise(() => {})))    // true
console.log(isPromise(Promise.reject('error')))  // true
console.log(isPromise({}))                       // false
console.log(isPromise([]))                       // false
console.log(isPromise(() => {}))                 // false

Syntax

typescript
function isPromise(value: any): value is Promise<any>

Parameters

  • value (any): The value to check

Return Value

Returns a boolean value, true if the value is a Promise object, false otherwise. Also serves as a TypeScript type guard.

Examples

Basic Type Checking

typescript
import { isPromise } from 'radash'

// Promise objects
console.log(isPromise(Promise.resolve(123)))     // true
console.log(isPromise(new Promise(() => {})))    // true
console.log(isPromise(Promise.reject('error')))  // true
console.log(isPromise(Promise.all([])))          // true
console.log(isPromise(Promise.race([])))         // true

// Non-Promise objects
console.log(isPromise({}))                       // false
console.log(isPromise([]))                       // false
console.log(isPromise(() => {}))                 // false
console.log(isPromise('hello'))                  // false
console.log(isPromise(123))                      // false
console.log(isPromise(true))                     // false
console.log(isPromise(null))                     // false
console.log(isPromise(undefined))                // false

Custom Promise Checking

typescript
import { isPromise } from 'radash'

// Custom Promise-like object
const customPromise = {
  then: (resolve: any) => resolve('custom'),
  catch: () => {}
}

console.log(isPromise(customPromise))            // false

// Real Promise
const realPromise = Promise.resolve('real')
console.log(isPromise(realPromise))              // true

Type Guard Usage

typescript
import { isPromise } from 'radash'

async function processValue(value: unknown) {
  if (isPromise(value)) {
    // TypeScript knows value is a Promise
    console.log('Processing Promise')
    return await value
  }
  
  console.log('Processing non-Promise:', typeof value)
  return value
}

const promise = Promise.resolve('hello')
const nonPromise = 'world'

console.log(await processValue(promise))     // Processing Promise hello
console.log(await processValue(nonPromise))  // Processing non-Promise: string world

Array Filtering

typescript
import { isPromise } from 'radash'

const mixedArray = [
  Promise.resolve(1),
  'hello',
  123,
  Promise.reject('error'),
  { then: () => {} },
  new Promise(() => {}),
  () => {},
  []
]

const promises = mixedArray.filter(isPromise)
console.log(promises.length) // 3

const nonPromises = mixedArray.filter(item => !isPromise(item))
console.log(nonPromises.length) // 5

Async Function Processing

typescript
import { isPromise } from 'radash'

function handleAsyncOperation(operation: unknown) {
  if (isPromise(operation)) {
    return operation.then(result => {
      console.log('Promise resolved:', result)
      return result
    }).catch(error => {
      console.log('Promise rejected:', error)
      throw error
    })
  }
  
  console.log('Non-Promise operation:', operation)
  return Promise.resolve(operation)
}

const promiseOp = Promise.resolve('success')
const syncOp = 'immediate'

handleAsyncOperation(promiseOp)  // Promise resolved: success
handleAsyncOperation(syncOp)     // Non-Promise operation: immediate

API Response Processing

typescript
import { isPromise } from 'radash'

interface ApiResponse {
  data: unknown
  error: unknown
  promise: unknown
}

async function processApiResponse(response: ApiResponse) {
  const processed = {
    data: isPromise(response.data) ? await response.data : response.data,
    error: isPromise(response.error) ? await response.error : response.error,
    promise: isPromise(response.promise) ? response.promise : Promise.resolve(response.promise)
  }
  
  return processed
}

const response: ApiResponse = {
  data: Promise.resolve({ id: 1, name: 'Alice' }),
  error: null,
  promise: 'not a promise'
}

console.log(await processApiResponse(response))
// { data: { id: 1, name: 'Alice' }, error: null, promise: Promise { 'not a promise' } }

Function Wrapping

typescript
import { isPromise } from 'radash'

function wrapWithPromise<T>(value: T | Promise<T>): Promise<T> {
  if (isPromise(value)) {
    return value
  }
  
  return Promise.resolve(value)
}

async function example() {
  const syncValue = 'hello'
  const asyncValue = Promise.resolve('world')
  
  const wrapped1 = wrapWithPromise(syncValue)
  const wrapped2 = wrapWithPromise(asyncValue)
  
  console.log(await wrapped1) // hello
  console.log(await wrapped2) // world
}

example()

Error Handling

typescript
import { isPromise } from 'radash'

async function safeExecute(operation: unknown) {
  try {
    if (isPromise(operation)) {
      return await operation
    }
    
    return operation
  } catch (error) {
    console.log('Error occurred:', error)
    return null
  }
}

const successPromise = Promise.resolve('success')
const errorPromise = Promise.reject('error')
const syncValue = 'hello'

console.log(await safeExecute(successPromise)) // success
console.log(await safeExecute(errorPromise))   // Error occurred: error null
console.log(await safeExecute(syncValue))      // hello

Batch Processing

typescript
import { isPromise } from 'radash'

async function processBatch(operations: unknown[]) {
  const results = []
  
  for (const operation of operations) {
    if (isPromise(operation)) {
      try {
        const result = await operation
        results.push({ success: true, data: result })
      } catch (error) {
        results.push({ success: false, error })
      }
    } else {
      results.push({ success: true, data: operation })
    }
  }
  
  return results
}

const batch = [
  Promise.resolve('success1'),
  'immediate',
  Promise.reject('error1'),
  Promise.resolve('success2'),
  123
]

console.log(await processBatch(batch))
// [
//   { success: true, data: 'success1' },
//   { success: true, data: 'immediate' },
//   { success: false, error: 'error1' },
//   { success: true, data: 'success2' },
//   { success: true, data: 123 }
// ]

Conditional Execution

typescript
import { isPromise } from 'radash'

async function conditionalExecute(condition: unknown, operation: unknown) {
  if (isPromise(condition)) {
    const shouldExecute = await condition
    if (shouldExecute) {
      return isPromise(operation) ? await operation : operation
    }
  } else if (condition) {
    return isPromise(operation) ? await operation : operation
  }
  
  return null
}

const trueCondition = Promise.resolve(true)
const falseCondition = Promise.resolve(false)
const syncCondition = true
const operation = Promise.resolve('executed')

console.log(await conditionalExecute(trueCondition, operation))  // executed
console.log(await conditionalExecute(falseCondition, operation)) // null
console.log(await conditionalExecute(syncCondition, operation))  // executed

Timeout Handling

typescript
import { isPromise } from 'radash'

async function withTimeout<T>(promise: T | Promise<T>, timeoutMs: number): Promise<T> {
  if (!isPromise(promise)) {
    return promise
  }
  
  const timeoutPromise = new Promise<never>((_, reject) => {
    setTimeout(() => reject(new Error('Timeout')), timeoutMs)
  })
  
  return Promise.race([promise, timeoutPromise])
}

const slowPromise = new Promise(resolve => {
  setTimeout(() => resolve('slow result'), 2000)
})

const fastPromise = Promise.resolve('fast result')

try {
  console.log(await withTimeout(slowPromise, 1000)) // Error: Timeout
} catch (error) {
  console.log('Timeout occurred')
}

console.log(await withTimeout(fastPromise, 1000)) // fast result

Retry Mechanism

typescript
import { isPromise } from 'radash'

async function withRetry<T>(
  operation: T | Promise<T>,
  maxRetries: number = 3,
  delay: number = 1000
): Promise<T> {
  if (!isPromise(operation)) {
    return operation
  }
  
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      return await operation
    } catch (error) {
      if (attempt === maxRetries) {
        throw error
      }
      
      await new Promise(resolve => setTimeout(resolve, delay))
    }
  }
  
  throw new Error('Max retries exceeded')
}

const failingPromise = new Promise((_, reject) => {
  setTimeout(() => reject('Failed'), 100)
})

const successPromise = Promise.resolve('Success')

try {
  console.log(await withRetry(successPromise)) // Success
  console.log(await withRetry(failingPromise, 3, 100)) // Error after 3 attempts
} catch (error) {
  console.log('All retries failed:', error)
}

Caching Mechanism

typescript
import { isPromise } from 'radash'

class PromiseCache {
  private cache = new Map<string, Promise<any>>()
  
  async getOrCreate<T>(
    key: string,
    factory: () => T | Promise<T>
  ): Promise<T> {
    if (this.cache.has(key)) {
      return this.cache.get(key)!
    }
    
    const promise = factory()
    if (isPromise(promise)) {
      this.cache.set(key, promise)
      return promise
    }
    
    const resolvedPromise = Promise.resolve(promise)
    this.cache.set(key, resolvedPromise)
    return resolvedPromise
  }
  
  clear() {
    this.cache.clear()
  }
}

const cache = new PromiseCache()

async function expensiveOperation(id: string) {
  console.log(`Executing expensive operation for ${id}`)
  await new Promise(resolve => setTimeout(resolve, 1000))
  return `Result for ${id}`
}

// First call executes the operation
console.log(await cache.getOrCreate('user1', () => expensiveOperation('user1')))
// Second call uses cache
console.log(await cache.getOrCreate('user1', () => expensiveOperation('user1')))

Concurrency Control

typescript
import { isPromise } from 'radash'

class ConcurrencyLimiter {
  private running = 0
  private queue: Array<() => Promise<any>> = []
  
  constructor(private maxConcurrency: number) {}
  
  async execute<T>(operation: T | Promise<T>): Promise<T> {
    if (!isPromise(operation)) {
      return operation
    }
    
    if (this.running >= this.maxConcurrency) {
      return new Promise((resolve, reject) => {
        this.queue.push(async () => {
          try {
            const result = await operation
            resolve(result)
          } catch (error) {
            reject(error)
          }
        })
      })
    }
    
    this.running++
    try {
      const result = await operation
      return result
    } finally {
      this.running--
      if (this.queue.length > 0) {
        const next = this.queue.shift()!
        next()
      }
    }
  }
}

const limiter = new ConcurrencyLimiter(2)

async function simulateWork(id: number) {
  console.log(`Starting work ${id}`)
  await new Promise(resolve => setTimeout(resolve, 1000))
  console.log(`Completed work ${id}`)
  return `Result ${id}`
}

// Execute concurrently, but max 2 at a time
const promises = Array.from({ length: 5 }, (_, i) => 
  limiter.execute(simulateWork(i + 1))
)

console.log(await Promise.all(promises))

Event Handling

typescript
import { isPromise } from 'radash'

class EventHandler {
  private handlers: Array<(...args: any[]) => any | Promise<any>> = []
  
  addHandler(handler: (...args: any[]) => any | Promise<any>) {
    this.handlers.push(handler)
  }
  
  async emit(...args: any[]) {
    const results = []
    
    for (const handler of this.handlers) {
      const result = handler(...args)
      
      if (isPromise(result)) {
        try {
          results.push(await result)
        } catch (error) {
          results.push({ error })
        }
      } else {
        results.push(result)
      }
    }
    
    return results
  }
}

const eventHandler = new EventHandler()

eventHandler.addHandler((data) => {
  console.log('Sync handler:', data)
  return 'sync result'
})

eventHandler.addHandler(async (data) => {
  console.log('Async handler:', data)
  await new Promise(resolve => setTimeout(resolve, 100))
  return 'async result'
})

eventHandler.emit('test data').then(results => {
  console.log('All handlers completed:', results)
})

Performance Testing

typescript
import { isPromise } from 'radash'

function benchmarkIsPromise() {
  const testValues = [
    Promise.resolve(1),
    Promise.reject('error'),
    new Promise(() => {}),
    {},
    [],
    () => {},
    'hello',
    123,
    true,
    null,
    undefined
  ]
  
  const iterations = 1000000
  const start = performance.now()
  
  for (let i = 0; i < iterations; i++) {
    testValues.forEach(value => {
      isPromise(value)
    })
  }
  
  const end = performance.now()
  console.log(`Benchmark completed in ${end - start}ms`)
}

// benchmarkIsPromise() // Run performance test

Notes

  1. Promise Detection: Accurately detects real Promise objects
  2. Type Guards: Serves as a TypeScript type guard
  3. Performance: Fast checking, suitable for high-frequency use
  4. Boundary Values: Correctly handles all boundary cases
  5. Custom Promises: Does not detect Promise-like objects

Differences from Other Methods

  • isPromise(): Checks if value is a Promise object
  • value instanceof Promise: Native JavaScript check
  • typeof value === 'object' && value !== null && typeof value.then === 'function': Manual check
  • value && typeof value.then === 'function': Simple but not accurate enough

Practical Application Scenarios

  1. Async Processing: Handle functions that may return Promises
  2. API Calls: Process asynchronous API responses
  3. Error Handling: Unify synchronous and asynchronous error handling
  4. Caching Mechanisms: Cache Promise results
  5. Concurrency Control: Limit concurrent Promise count
  6. Event Handling: Handle asynchronous event handlers

Released under the MIT License.