Overview
The fromSafePromise function converts a Promise<T> that you know will never reject into a ResultAsync<T, E>. Unlike fromPromise, it does not require an error handler because it assumes the promise will always resolve successfully.
fromSafePromise is a top-level export that references ResultAsync.fromSafePromise.
Use with CautionOnly use this function when you are absolutely certain the promise will not reject. If the promise does reject, the ResultAsync will reject as well, breaking the Result abstraction. When in doubt, use fromPromise instead.
Type Signature
function fromSafePromise<T, E = never>(
promise: PromiseLike<T>
): ResultAsync<T, E>
Parameters
promise: A PromiseLike<T> that is guaranteed to never reject
Return Value
Returns a ResultAsync<T, E> that resolves to Ok<T> containing the promise’s resolved value.
When to Use
Use fromSafePromise when:
- You’re working with
Promise.resolve() or similar guaranteed-to-succeed promises
- You’ve wrapped a promise in a try-catch that ensures it never rejects
- You’re using a library that returns promises but documents they never reject
- You want to convert a value to
ResultAsync for API consistency
Basic Usage
Converting a Simple Delay
import { fromSafePromise } from 'neverthrow'
function delay<T>(ms: number, value: T): ResultAsync<T, never> {
return fromSafePromise(
new Promise(resolve => setTimeout(() => resolve(value), ms))
)
}
const result = await delay(1000, 'hello')
console.log(result._unsafeUnwrap()) // 'hello' after 1 second
Working with asyncMap
import { ok } from 'neverthrow'
const result = ok(5)
.asyncMap(async (n) => {
// asyncMap internally uses fromSafePromise
await new Promise(resolve => setTimeout(resolve, 100))
return n * 2
})
const value = await result
console.log(value._unsafeUnwrap()) // 10
Real-World Examples
Rate Limiting
import { fromSafePromise, ResultAsync } from 'neverthrow'
export function slowDown<T>(ms: number) {
return (value: T): ResultAsync<T, never> =>
fromSafePromise(
new Promise<T>((resolve) => {
setTimeout(() => resolve(value), ms)
})
)
}
// Usage in a processing pipeline
const result = await processUser(user)
.andThen(slowDown(3000)) // Slow down by 3 seconds
.andThen(saveUser)
Simulating Slow Operations
import { fromSafePromise } from 'neverthrow'
// Simulate slow network in development
function simulateLatency<T>(value: T, ms: number = 500) {
if (process.env.NODE_ENV !== 'development') {
return okAsync(value)
}
return fromSafePromise(
new Promise<T>(resolve => setTimeout(() => resolve(value), ms))
)
}
const user = await fetchUser(userId)
.andThen(user => simulateLatency(user, 1000))
.andThen(validateUser)
Promise.resolve Wrapper
import { fromSafePromise } from 'neverthrow'
// Convert a synchronous value to ResultAsync for API consistency
function toResultAsync<T>(value: T): ResultAsync<T, never> {
return fromSafePromise(Promise.resolve(value))
}
// Usage: Mix sync and async operations seamlessly
function processData(useCache: boolean) {
if (useCache) {
// Return cached value as ResultAsync
return toResultAsync(getCachedData())
} else {
// Fetch fresh data
return fetchData()
}
}
Debounced Operations
import { fromSafePromise, ResultAsync } from 'neverthrow'
function debounce<T>(fn: () => T, ms: number): ResultAsync<T, never> {
return fromSafePromise(
new Promise<T>((resolve) => {
setTimeout(() => resolve(fn()), ms)
})
)
}
const debouncedSearch = (query: string) =>
debounce(() => performSearch(query), 300)
Testing Utilities
import { fromSafePromise, ResultAsync } from 'neverthrow'
// Mock async functions in tests
function mockAsyncOk<T>(value: T): ResultAsync<T, never> {
return fromSafePromise(Promise.resolve(value))
}
function mockAsyncErr<E>(error: E): ResultAsync<never, E> {
return errAsync(error)
}
// In tests
test('handles user creation', async () => {
const mockCreateUser = jest.fn()
.mockReturnValue(mockAsyncOk({ id: '123', name: 'John' }))
const result = await processUserCreation(mockCreateUser)
expect(result.isOk()).toBe(true)
})
Animation Delays
import { fromSafePromise, safeTry } from 'neverthrow'
function animateElement(element: HTMLElement, duration: number) {
return safeTry(async function*() {
// Start animation
element.classList.add('animating')
// Wait for animation
yield* fromSafePromise(
new Promise<void>(resolve => setTimeout(resolve, duration))
)
// Clean up
element.classList.remove('animating')
return ok(undefined)
})
}
Batch Processing with Delays
import { fromSafePromise, ResultAsync } from 'neverthrow'
function processBatch<T, E>(
items: T[],
processor: (item: T) => ResultAsync<void, E>,
delayMs: number = 100
): ResultAsync<void, E> {
return items.reduce(
(acc, item) =>
acc
.andThen(() => processor(item))
.andThen(() =>
fromSafePromise(
new Promise<void>(resolve => setTimeout(resolve, delayMs))
)
),
okAsync<void, E>(undefined)
)
}
// Process 100 items with 100ms delay between each
await processBatch(items, processItem, 100)
Internal Usage
Many Result/ResultAsync methods use fromSafePromise internally:
asyncMap Implementation
// Inside the Ok class
asyncMap<U>(f: (t: T) => Promise<U>): ResultAsync<U, E> {
// Uses fromSafePromise because f is expected to return a resolved promise
return ResultAsync.fromSafePromise(f(this.value))
}
Comparison with fromPromise
Using fromPromise (Safe, Verbose)
import { fromPromise } from 'neverthrow'
const result = fromPromise(
new Promise(resolve => setTimeout(() => resolve(42), 1000)),
() => 'This will never happen' // Required but unnecessary
)
Using fromSafePromise (Less Safe, Concise)
import { fromSafePromise } from 'neverthrow'
const result = fromSafePromise(
new Promise(resolve => setTimeout(() => resolve(42), 1000))
)
// No error handler needed
Type Safety
import { fromSafePromise } from 'neverthrow'
// Error type defaults to 'never'
const result1 = fromSafePromise(Promise.resolve(42))
// result1 type: ResultAsync<number, never>
// You can specify error type for API consistency
const result2 = fromSafePromise<number, string>(Promise.resolve(42))
// result2 type: ResultAsync<number, string>
// (even though it will never have a string error)
Common Pitfalls
Don’t Use with Potentially Failing Operations// BAD: fetch can reject
const result = fromSafePromise(
fetch('https://api.example.com/data')
)
// If fetch fails, ResultAsync will reject (breaks abstraction)
// GOOD: Use fromPromise instead
const result = fromPromise(
fetch('https://api.example.com/data'),
() => 'FetchError'
)
Don’t Use with Untrusted Promises// BAD: Third-party library might reject
const result = fromSafePromise(
untrustedLibrary.doSomething()
)
// GOOD: Wrap in error handling
const result = fromPromise(
untrustedLibrary.doSomething(),
(error) => handleError(error)
)
Key Points
- Only use when promise is guaranteed to never reject
- No error handler required
- Error type defaults to
never
- Useful for delays, timeouts, and Promise.resolve
- Used internally by
asyncMap and similar methods
- When in doubt, use
fromPromise instead
- If the promise rejects, the entire ResultAsync abstraction breaks