sleep 函数在React项目中的运用

0 阅读2分钟

核心原理

function sleep(delay) {
    return new Promise(resolve => setTimeout(resolve, delay))
}

作用:创建一个延迟指定毫秒数后才会 resolve 的 Promise,用于实现异步等待。

执行流程

function sleep(delay) {
    return new Promise(resolve => setTimeout(resolve, delay))
}

// 执行流程可视化
console.log('1. 开始执行')
sleep(2000).then(() => {
    console.log('3. 2秒后执行')
})
console.log('2. 立即执行,不阻塞')

// 输出顺序:
// 1. 开始执行
// 2. 立即执行,不阻塞
// 3. 2秒后执行

详细分析

1. Promise 构造器

new Promise(resolve => setTimeout(resolve, delay))
  • resolve 是 Promise 提供的回调函数
  • setTimeout 在延迟后调用 resolve
  • 当 resolve 被调用时,Promise 状态变为 fulfilled

2. 返回值

返回一个 Promise 对象,可以链式调用 .then() 或使用 async/await

使用方式

方式1:Promise.then()

function sleep(delay) {
    return new Promise(resolve => setTimeout(resolve, delay))
}

console.log('开始')
sleep(1000).then(() => {
    console.log('1秒后')
    return sleep(1000)
}).then(() => {
    console.log('2秒后')
})

方式2:async/await

function sleep(delay) {
    return new Promise(resolve => setTimeout(resolve, delay))
}

async function demo() {
    console.log('开始')
    await sleep(1000)
    console.log('1秒后')
    await sleep(1000)
    console.log('2秒后')
}

demo()

方式3:配合循环使用

function sleep(delay) {
    return new Promise(resolve => setTimeout(resolve, delay))
}

async function countdown(seconds) {
    for (let i = seconds; i > 0; i--) {
        console.log(`${i} 秒...`)
        await sleep(1000)
    }
    console.log('时间到!')
}

countdown(5)

实际应用场景

场景1:轮询

function sleep(delay) {
    return new Promise(resolve => setTimeout(resolve, delay))
}

async function pollData() {
    let retries = 3
    
    while (retries > 0) {
        try {
            const data = await fetchData()
            if (data) return data
            console.log('数据未就绪,1秒后重试...')
            await sleep(1000)
            retries--
        } catch (error) {
            console.log('请求失败,重试中...')
            await sleep(2000)
            retries--
        }
    }
    throw new Error('获取数据失败')
}

场景2:限流/节流

function sleep(delay) {
    return new Promise(resolve => setTimeout(resolve, delay))
}

async function rateLimitedRequests(urls) {
    const results = []
    
    for (const url of urls) {
        const result = await fetch(url)
        results.push(result)
        await sleep(1000)  // 每秒最多请求一次
    }
    
    return results
}

场景3:模拟加载

function sleep(delay) {
    return new Promise(resolve => setTimeout(resolve, delay))
}

async function simulateLoading() {
    console.log('加载中...')
    await sleep(2000)
    console.log('✅ 加载完成')
    
    console.log('处理数据...')
    await sleep(1500)
    console.log('✅ 处理完成')
    
    console.log('保存结果...')
    await sleep(1000)
    console.log('✅ 保存完成')
}

simulateLoading()

场景4:超时控制

function sleep(delay) {
    return new Promise(resolve => setTimeout(resolve, delay))
}

function timeout(promise, ms) {
    return Promise.race([
        promise,
        sleep(ms).then(() => {
            throw new Error('操作超时')
        })
    ])
}

// 使用
async function fetchWithTimeout() {
    try {
        const data = await timeout(fetch('https://api.example.com'), 3000)
        console.log('数据:', data)
    } catch (error) {
        console.log('超时错误:', error.message)
    }
}

边界情况

1. delay 为 0

function sleep(delay) {
    return new Promise(resolve => setTimeout(resolve, delay))
}

console.log('1. 同步代码')
sleep(0).then(() => console.log('3. 微任务后执行'))
console.log('2. 同步代码')

// 输出:
// 1. 同步代码
// 2. 同步代码
// 3. 微任务后执行

2. 负数 delay

function sleep(delay) {
    return new Promise(resolve => setTimeout(resolve, delay))
}

// 负数会被转换为0或接近0
sleep(-1000).then(() => console.log('立即执行(微任务)'))
console.log('同步代码')

3. 非常大的 delay

function sleep(delay) {
    return new Promise(resolve => setTimeout(resolve, delay))
}

// 最大延迟约 24.8 天(2^31-1 毫秒)
sleep(2 ** 31 - 1).then(() => console.log('永远不会执行?'))
// setTimeout 最大延迟约 24.8 天

性能分析

1. 精度问题

function sleep(delay) {
    return new Promise(resolve => setTimeout(resolve, delay))
}

async function measurePrecision() {
    const start = Date.now()
    await sleep(1000)
    const actualDelay = Date.now() - start
    console.log(`期望: 1000ms, 实际: ${actualDelay}ms`)
    console.log(`误差: ${actualDelay - 1000}ms`)
}

measurePrecision()
// 通常在 1000-1010ms 左右(4-10ms 误差)

2. 内存占用

function sleep(delay) {
    return new Promise(resolve => setTimeout(resolve, delay))
}

// 大量并发 sleep
async function manySleeps() {
    const promises = []
    for (let i = 0; i < 10000; i++) {
        promises.push(sleep(1000))
    }
    await Promise.all(promises)
    console.log('所有 sleep 完成')
}
// 会创建 10000 个定时器,内存占用较大

增强版本

版本1:支持取消

function sleep(delay) {
    let timeoutId
    const promise = new Promise(resolve => {
        timeoutId = setTimeout(resolve, delay)
    })
    
    promise.cancel = () => {
        clearTimeout(timeoutId)
    }
    
    return promise
}

// 使用
const sleepPromise = sleep(5000)
sleepPromise.then(() => console.log('不会执行'))

// 3秒后取消
setTimeout(() => {
    sleepPromise.cancel()
    console.log('已取消')
}, 3000)

版本2:支持值传递

function sleep(delay, value) {
    return new Promise(resolve => setTimeout(() => resolve(value), delay))
}

// 使用
sleep(1000, '完成').then(result => {
    console.log(result)  // 1秒后输出 '完成'
})

// 配合 async/await
async function demo() {
    const result = await sleep(2000, '数据已加载')
    console.log(result)
}

版本3:带状态提示

function sleep(delay, options = {}) {
    const { signal, onStart, onEnd } = options
    
    return new Promise((resolve, reject) => {
        if (signal?.aborted) {
            return reject(new Error('已取消'))
        }
        
        onStart?.()
        
        const timeoutId = setTimeout(() => {
            onEnd?.()
            resolve()
        }, delay)
        
        signal?.addEventListener('abort', () => {
            clearTimeout(timeoutId)
            reject(new Error('已取消'))
        })
    })
}

// 使用 AbortController
const controller = new AbortController()
sleep(5000, { signal: controller.signal })
    .then(() => console.log('完成'))
    .catch(e => console.log('取消:', e.message))

setTimeout(() => controller.abort(), 2000)

版本4:精确延迟(使用 performance)

function sleepPrecise(delay) {
    const start = performance.now()
    return new Promise(resolve => {
        function tick(now) {
            if (now - start >= delay) {
                resolve()
            } else {
                requestAnimationFrame(tick)
            }
        }
        requestAnimationFrame(tick)
    })
}

// 更精确但更耗 CPU
async function test() {
    const start = performance.now()
    await sleepPrecise(1000)
    const actual = performance.now() - start
    console.log(`精确延迟: ${actual.toFixed(2)}ms`)
}

与其他实现对比

// 1. 你的实现
function sleep(delay) {
    return new Promise(resolve => setTimeout(resolve, delay))
}

// 2. 使用 setTimeout 直接返回
function sleep2(delay) {
    return new Promise(resolve => {
        setTimeout(resolve, delay)
    })
}

// 3. 使用 async/await 包装(多余)
async function sleep3(delay) {
    return new Promise(resolve => setTimeout(resolve, delay))
}

// 4. 带错误处理
function sleep4(delay) {
    if (typeof delay !== 'number' || delay < 0) {
        return Promise.reject(new Error('delay must be a positive number'))
    }
    return new Promise(resolve => setTimeout(resolve, delay))
}

// 性能测试
async function comparePerformance() {
    const iterations = 1000
    
    console.time('sleep 原始')
    for (let i = 0; i < iterations; i++) {
        await sleep(10)
    }
    console.timeEnd('sleep 原始')
    
    console.time('sleep4 带检查')
    for (let i = 0; i < iterations; i++) {
        await sleep4(10)
    }
    console.timeEnd('sleep4 带检查')
}

常见使用模式

1. 顺序延迟

async function sequentialDelay() {
    console.log('步骤1')
    await sleep(1000)
    console.log('步骤2')
    await sleep(1000)
    console.log('步骤3')
}

2. 并发延迟

async function concurrentDelay() {
    const promises = [
        sleep(1000).then(() => '任务1'),
        sleep(2000).then(() => '任务2'),
        sleep(3000).then(() => '任务3')
    ]
    const results = await Promise.all(promises)
    console.log('所有任务完成:', results)
}

3. 指数退避

async function exponentialBackoff(fn, maxRetries = 5) {
    let delay = 1000
    
    for (let i = 0; i < maxRetries; i++) {
        try {
            return await fn()
        } catch (error) {
            if (i === maxRetries - 1) throw error
            
            console.log(`第 ${i + 1} 次重试,等待 ${delay}ms...`)
            await sleep(delay)
            delay *= 2  // 指数增长
        }
    }
}

总结

 sleep 函数:

✅ 优点

  1. 简洁优雅:一行代码实现核心功能
  2. 性能良好:使用原生 Promise 和 setTimeout
  3. 易于使用:支持 then 和 async/await
  4. 无副作用:纯函数,不修改外部状态

✅ 应用场景

  • 测试异步代码
  • 实现轮询
  • 控制请求频率
  • 模拟耗时操作
  • 动画和过渡效果
  • 超时控制

⚠️ 注意事项

  • 精度约为 4-10ms(受事件循环影响)
  • 大量并发可能占用较多定时器资源
  • 不会阻塞事件循环(非阻塞延迟)