从2024年2月起,函数计算正式发布 Node.js 18 运行时和 Nodejs.20 运行时,函数计算2.0和函数计算3.0都支持新的运行时,目前新运行时处在公测状态,欢迎大家来体验。
目前支持 Node.js 18 和 Nodejs.js 20 运行时的地域,参考文档:Node.js 环境说明
下面介绍 Node.js 18 和 Nodejs.js 20 运行时的主要变化。
新特性介绍
支持 ECMAScript (ES) 模块
函数计算从 Node.js 18 运行时开始支持 ECMAScript(ES)模块。在此之前(Node.js 16及以前的版本),函数计算仅支持使用 CommonJS 模块。
您可以通过以下两种方式将代码指定为 ES 模块。
第一种方法是将函数的 package.json 文件中将 type指定为 module,这种方式会将所有.js文件指定为 ES 模块。例如:CommonJS 模块:
// package.json { "name": "fc-es-module-example", "type": "module", "description": "This package will be treated as an ES module.", "version": "1.0.0", "main": "index.js", "author": "Aliyun FC", "license": "ISC" }
// index.js – this file will inherit the type from import { hello } from './lib.mjs'; export const handler = async (event, context) => { let result = hello("FC"); // Hello FC return result; } // lib.mjs export function hello(name) { return `Hello ${name}!`; }
第二种方法是使用.mjs
文件扩展名,这种方式会将.mjs
文件视为 ES 模块,.js
文件仍然视为 CommonJS 模块。您可以通过将文件分别命名为.mjs
和.cjs
来混合 ES 模块和 CommonJS 模块,因为.mjs
文件始终是 ES 模块,.cjs
文件始终是 CommonJS模块。
// this file is named index.mjs – it will always be treated as an ES module import { square } from './lib.mjs'; export const handler = async (event, context) => { let result = hello("FC"); // Hello FC return result; } // lib.mjs export function hello(name) { return `Hello ${name}!`; }
异步编程接口优化
如果您的代码是执行异步任务,推荐使用 Async/await 的异步编程方式来确保处理程序执行完成。Async/await 是一种简洁、易读的 Node.js 异步编程方式,不需要使用嵌套回调或链式调用。使用 Async/await 的方式,编写的代码与同步代码类似,同时仍然能实现异步和非阻塞的功能。
async 关键字会将函数标记为异步, await 关键字会暂停函数的执行,直到 Promise 完成解析为止。
相比于回调函数 callback ,Async/await 方式具有以下优点:
- 可读性更好:Async/Await方式的代码更加线性和同步,更易于理解和维护。它避免了回调函数嵌套过深的情况,使代码结构更清晰。
- 调试和错误处理更简单:可以使用try-catch块更方便地捕获和处理异步操作中的错误,错误堆栈更加清晰,可以更准确地追踪错误发生的位置。
- 效率更高:回调函数通常需要在代码的不同部分之间切换。Async/Await方式可以减少上下文切换的数量,从而提高代码效率。
使用 return 代替 callback 返回响应结果
在 Node.js 16 运行时版本之前,在请求处理程序(Handler)中必须显式使用回调函数 callback 来返回结果,不支持使用 return ,比如:
exports.handler = function(event, context, callback) { callback(null, 'hello world'); };
如果没有显示调用 callback,函数计算会一直等待,直到请求超时返回报错
{ "errorMessage": "Function timed out after 6 seconds (maxMemoryUsage: 0MB)" }
从 Node.js 18 开始,Node.js 运行时支持直接使用 return 返回结果,无论是 ES 模块还是 CommonJS 模块都支持这种方式,比如
// ES Module export const handler = async (event, context) => { console.log("event: \n" + event); return "Hello World!"; }; // CommonJS Module exports.handler = async function(event, context) { console.log("event: \n" + event); return "Hello World!"; };
我们强烈推荐使用 return 来返回结果,而不是 callback返回结果。
实例冻结机制
在编写异步的处理程序时仍需要注意一点,在函数返回之前,要确保等待异步事件执行完成,否则该函数可能会失败或者出现意外行为。
这里的主要是因为函数的实例冻结机制,当函数实例没有请求处理时,实例会被 冻结(Freeze) ,当新的请求到来时,函数计算会将实例恢复(Thaw)。在实例冻结期间,程序的后台任务会被暂停运行,如果有异步调用还未执行完成,函数可能会出现失败。
实例冻结机制使用 CGroup freeze 实现。当进程被冻结时,会暂停进程在CPU上运行,但其状态仍然保存在内存中,可以在需要时快速恢复。
例如下面的示例
var count = 0; async function calc(num) { let result = count + num; await new Promise((resolve) => setTimeout(resolve, 100)); // 模拟异步操作 count = result; } export const handler = async (event, context) => { count = 0; calc(100); return "count: " + count; }
当没有使用await calc(100)时,收到的响应结果是 count: 0,所以,一定要 await 异步任务执行结束再返回。
支持使用环境变量 TZ 设置日志时区
在使用Node.js 运行时打印日志时,默认是使用UTC时间,比如以下代码打印日志
console.log('Hello World!');
输入日志如下所示(北京时间为 2024-02-29 11:46:02)
2024-02-29 03:46:02.259 1-65dffdfa-xxxxxxx [info] Hello World!
当使用 Node.js 18 和 Nodejs.js 20 运行时,我们支持通过环境变量 TZ 设置日志时区,例如设置为 TZ=Asia/Shanghai 时,输出日志如下所示:
2024-02-29 11:50:03.387 1-65dffeeb-xxxxxxx [info] Hello World!
注意:Node.js 16 运行时及之前的版本不支持使用 TZ 设置日志时区。
总结
本文简单介绍函数计算新支持的Node.js 18 和 Nodejs.js 20 运行时的主要变化,以及使用新运行时开发时的技巧和注意事项。在使用中有任何疑问或建议,欢迎加入钉钉用户群(钉钉群号:11721331)联系我们。