Nodejs promise.timeout 现在支持资源清理了

发布于 1周前 作者 songsunli 来自 nodejs/Nestjs

Nodejs promise.timeout 现在支持资源清理了

原文发在 CNodejs.org https://cnodejs.org/topic/574f88811176fe255bb8d7e3


promise.timeout 是我之前发的 promise 工具包 系列 https://cnodejs.org/topic/573b2d64fcf698421d20359d 中的一个

promise.timeout

promise.timeout 给一个 async function 加上超时支持, 就是你原来的返回值是一 promise, 加上 timeout 支持后, 超时了就会以一个 timeout error reject 掉 https://github.com/magicdawn/promise.timeout

function test(){
  // 20ms 后去 resolve
  return new Promise(resolve => {
    setTimeout(()=>{ resolve(20) }, 20);
  });
}

const ptimeout = require(‘promise.timeout’); const test10 = ptimeout(test, 10); // test 方法, 10ms 超时

// e instanceof ptimeout.TimeoutError test10().then(function(res){ console.log(res); }, function(e){ console.error(e) });

问题

这里其实是这样的 untitled1.png

原来 test() 和 超时定时器比谁先跑完, 10ms 的超时定时器先跑完. 于是 test10() 的返回值以 ptimeout.TimeoutError 实例 reject 掉 但是这里有个问题, promise 没有取消机制, 原来的 promise 还在跑...

onCancel

function test(onCancel){
  // 20ms 后去 resolve
  return new Promise(resolve => {
    const t = setTimeout(()=>{ resolve(20) }, 20);
	onCancel && onCancel(() => {
	  clearTimeout(t); // 资源清理
	});
  });
}

const ptimeout = require(‘promise.timeout’); const test10 = ptimeout(test, 10, true); // test 方法, 10ms 超时, 加了一个 cancel = true 参数

// e instanceof ptimeout.TimeoutError test10().then(function(res){ console.log(res); }, function(e){ console.error(e) });

如果给 ptimeout 的第三个参数传 true, 表示打开 cancel 支持, 这样

  • test10 在 调用 test 的时候, 会多传一个 onCancel 参数
  • test 可以拿 onCancel 注册回调, 在超时器先跑完的情况下, 可以去清理你这个 promise 所占有的资源
  • 然后在没超时的情况下, test() 的返回值可以清理掉超时定时器, 避免因为 timer 引起的进程不退出, 也是避免调用那个清理操作.

其实就是两个 race, 谁先跑完就把另一个清理掉... 这里拿 timer 举例的, 还可以看看 网络请求 / 文件访问这种, 如果超时, 可以 abort 网络请求, close 文件. 实例请看 https://github.com/magicdawn/yun-playlist-downloader


1 回复

在Node.js中,处理Promise超时和资源清理是一个常见的需求。虽然Node.js原生Promise没有内置的.timeout方法,但我们可以通过一些库(如bluebirdp-timeout)或自定义实现来实现这一功能,并结合finallyasync/await进行资源清理。

以下是一个使用p-timeout库实现Promise超时和资源清理的示例:

首先,安装p-timeout库:

npm install p-timeout

然后,编写代码:

const pTimeout = require('p-timeout');
const fs = require('fs').promises;

async function readFileWithTimeout(filePath, timeout) {
    try {
        // 尝试在指定时间内读取文件
        const data = await pTimeout(fs.readFile(filePath), timeout);
        console.log('File read successfully:', data.toString());
    } catch (error) {
        if (error.code === 'ETIMEDOUT') {
            console.error('Reading file timed out');
        } else {
            console.error('Error reading file:', error);
        }
    } finally {
        // 清理资源,例如关闭文件句柄(在这个例子中,fs.promises自动管理)
        console.log('Cleanup executed');
    }
}

// 使用示例
readFileWithTimeout('./example.txt', 2000); // 2秒超时

在上面的代码中,pTimeout用于在指定时间内等待Promise完成。如果超时,它将抛出一个带有ETIMEDOUT代码的错误。finally块确保无论Promise是否成功或超时,清理代码都会执行。在这个例子中,由于使用了fs.promises,文件句柄的关闭是自动管理的,但finally块可以用于执行其他清理任务。

回到顶部