{"componentChunkName":"component---src-templates-blog-detail-tsx","path":"/blog/2020-05-12-nodejs-runtime","result":{"data":{"currentBlog":{"id":"38503518-dfcb-58ff-8b5a-f545d5459e6b","frontmatter":{"thumbnail":"https://img.serverlesscloud.cn/2020518/1589791111276-node12%E5%89%AF%E6%9C%AC.jpg","authors":["Wes"],"categories":["guides-and-tutorials"],"date":"2020-05-12T00:00:00.000Z","title":"云函数 SCF Node.js Runtime 最佳实践","description":"如何使用腾讯云云函数来开发 Node.js 应用以及云函数的 Node.js runtime 实现原理","authorslink":["https://zhuanlan.zhihu.com/ServerlessGo"],"translators":null,"translatorslink":null,"tags":["Node.js","云函数"],"keywords":"Serverless,scf,Node.js","outdated":null},"wordCount":{"words":313,"sentences":54,"paragraphs":54},"fileAbsolutePath":"/opt/build/repo/content/blog/2020-05-12-nodejs-runtime.md","fields":{"slug":"/blog/2020-05-12-nodejs-runtime/","keywords":["java","serverless","spa","云函数","函数","异步","callback","返回","Node","event","context"]},"html":"<p>腾讯云云函数 SCF 最近新发布了 Node.js 12.16 的 runtime，也是国内首家支持 Node.js 12.x 的主流云服务商。</p>\n<p>Node.js 版本的升级带来了新的特性以及性能方面的提升，有兴趣的同学可以参考国外一博主总结的文章<a href=\"https://blog.logrocket.com/node-js-12/\">《Node.js 12: The future of server-side JavaScript》</a>了解具体内容。</p>\n<p>其中比较重要的一点是启动速度提升，通过 <code class=\"language-text\">v8 code cache</code> 的支持，构建时提前为内置库生成代码缓存，提升 30% 的启动耗时。</p>\n<p>腾讯云云函数 SCF 为了让 Serverless 更加符合 Node.js 原生的使用体验，针对 Node.js runtime 做了针对性的优化。\n借这个机会，我想和大家分享一下如何使用腾讯云云函数来开发 Node.js 应用以及 scf 的 Node.js runtime 实现的原理。</p>\n<h2 id=\"入口函数的参数\"><a href=\"#%E5%85%A5%E5%8F%A3%E5%87%BD%E6%95%B0%E7%9A%84%E5%8F%82%E6%95%B0\" aria-label=\"入口函数的参数 permalink\" class=\"anchor\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>入口函数的参数</h2>\n<p>首先我们看一下最基本的 Node.js 入口函数：</p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"37583914497687390000\"\n              data-toaster-class=\"gatsby-code-button-toaster\"\n              data-toaster-text-class=\"gatsby-code-button-toaster-text\"\n              data-toaster-text=\"代码复制成功\"\n              data-toaster-duration=\"3500\"\n              onClick=\"copyToClipboard(`exports.main_handler = (event, context, callback) => {\n  console.log(&quot;Hello World&quot;);\n  console.log(event);\n  console.log(context);\n  callback(null, event);\n};`, `37583914497687390000`)\"\n            >\n              <div\n                class=\"gatsby-code-button\"\n                data-tooltip=\"\"\n              >\n                复制代码<svg class=\"gatsby-code-button-icon\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\"><path fill=\"none\" d=\"M0 0h24v24H0V0z\"/><path d=\"M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z\"/></svg>\n              </div>\n            </div>\n<div class=\"gatsby-highlight\" data-language=\"javascript\"><pre class=\"language-javascript\"><code class=\"language-javascript\">exports<span class=\"token punctuation\">.</span><span class=\"token function-variable function\">main_handler</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">event<span class=\"token punctuation\">,</span> context<span class=\"token punctuation\">,</span> callback</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n  console<span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"Hello World\"</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  console<span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span>event<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  console<span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span>context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token function\">callback</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span> event<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span></code></pre></div>\n<p>runtime 会将三个参数传递到处理程序方法。</p>\n<h3 id=\"第一个参数是-event，用来传递触发事件数据\"><a href=\"#%E7%AC%AC%E4%B8%80%E4%B8%AA%E5%8F%82%E6%95%B0%E6%98%AF-event%EF%BC%8C%E7%94%A8%E6%9D%A5%E4%BC%A0%E9%80%92%E8%A7%A6%E5%8F%91%E4%BA%8B%E4%BB%B6%E6%95%B0%E6%8D%AE\" aria-label=\"第一个参数是 event，用来传递触发事件数据 permalink\" class=\"anchor\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>第一个参数是 event，用来传递触发事件数据</h3>\n<p>包含来自调用程序的信息。调用程序在调用时将该信息作为 JSON 格式字符串传递，事件结构因服务而异。</p>\n<ul>\n<li>定时触发器的 event 对象就包括了触发的时间，触发器的名称\n<code class=\"language-text\">{Message: &quot;&quot;, Time: &quot;2020-05-08T14:30:00Z&quot;, TriggerName: &quot;time_5&quot;, Type: &quot;Timer&quot;}</code></li>\n<li>apigateway 触发器的 event 对象透传了 http 请求的完整内容以及 apigateway 定制化的 http 请求头部信息</li>\n</ul>\n<p><code class=\"language-text\">{&quot;headerParameters&quot;:{},&quot;headers&quot;:{...},&quot;httpMethod&quot;:&quot;GET&quot;,&quot;path&quot;:&quot;/params_log&quot;,&quot;pathParameters&quot;:{},&quot;queryString&quot;:{},&quot;queryStringParameters&quot;:{},&quot;requestContext&quot;:{&quot;httpMethod&quot;:&quot;ANY&quot;,&quot;identity&quot;:{},&quot;path&quot;:&quot;/params_log&quot;,&quot;serviceId&quot;:&quot;service-9khp96qy&quot;,&quot;sourceIp&quot;:&quot;120.229.9.165&quot;,&quot;stage&quot;:&quot;release&quot;}}</code></p>\n<ul>\n<li>cos 触发器的 event 对象包括了触发执行的具体 cos 操作以及 cos 对象</li>\n</ul>\n<p><code class=\"language-text\">{&quot;Records&quot;:[{&quot;cos&quot;:{&quot;cosBucket&quot;:{&quot;appid&quot;:&quot;1251133793&quot;,&quot;name&quot;:&quot;test&quot;,&quot;region&quot;:&quot;gz&quot;},&quot;cosNotificationId&quot;:&quot;unkown&quot;,&quot;cosObject&quot;:{&quot;key&quot;:&quot;/1251133793/test/xxx.png&quot;,&quot;meta&quot;:{&quot;Content-Type&quot;:&quot;image/png&quot;,&quot;x-cos-request-id&quot;:&quot;NWViNTZmMmFfOTJhODQwYV80MGZmXzI0Y2ZkYmM=&quot;},&quot;size&quot;:6545739,&quot;url&quot;:&quot;...&quot;,&quot;vid&quot;:&quot;&quot;},&quot;cosSchemaVersion&quot;:&quot;1.0&quot;},&quot;event&quot;:{&quot;eventName&quot;:&quot;cos:ObjectCreated:Put&quot;,&quot;eventQueue&quot;:&quot;qcs:0:scf:ap-guangzhou:appid/1251133793:default.params_log.$DEFAULT&quot;,&quot;eventSource&quot;:&quot;qcs::cos&quot;,&quot;eventTime&quot;:1588948779,&quot;eventVersion&quot;:&quot;1.0&quot;,&quot;reqid&quot;:1038862404,&quot;requestParameters&quot;:{&quot;requestHeaders&quot;:{&quot;Authorization&quot;:&quot;...&quot;},&quot;requestSourceIP&quot;:&quot;120.229.9.165&quot;},&quot;reservedInfo&quot;:&quot;&quot;}}]}</code></p>\n<h3 id=\"第二个参数-context，函数运行时信息\"><a href=\"#%E7%AC%AC%E4%BA%8C%E4%B8%AA%E5%8F%82%E6%95%B0-context%EF%BC%8C%E5%87%BD%E6%95%B0%E8%BF%90%E8%A1%8C%E6%97%B6%E4%BF%A1%E6%81%AF\" aria-label=\"第二个参数 context，函数运行时信息 permalink\" class=\"anchor\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>第二个参数 context，函数运行时信息</h3>\n<p>我们来看一下一个完整的 context 包含的内容：</p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"2112296017722825700\"\n              data-toaster-class=\"gatsby-code-button-toaster\"\n              data-toaster-text-class=\"gatsby-code-button-toaster-text\"\n              data-toaster-text=\"代码复制成功\"\n              data-toaster-duration=\"3500\"\n              onClick=\"copyToClipboard(`callbackWaitsForEmptyEventLoop: true,\ngetRemainingTimeInMillis: 200,\nmemory_limit_in_mb: 128,\ntime_limit_in_ms: 3000,\nenvironment: &quot;{&quot;SCF_NAMESPACE&quot;:&quot;demo&quot;,&quot;TENCENTCLOUD_SECRETID&quot;:&quot;...&quot;,&quot;TENCENTCLOUD_SECRETKEY&quot;:&quot;...&quot;,&quot;TENCENTCLOUD_SESSIONTOKEN&quot;:&quot;...&quot;}&quot;\nfunction_name: &quot;params&quot;,\nfunction_version: &quot;\\$LATEST&quot;,\nnamespace: &quot;demo&quot;,\nrequest_id: &quot;ab42b693-8bfd-4dc1-b228-60360a63e06c&quot;,\ntencentcloud_appid: &quot;...&quot;,\ntencentcloud_region: &quot;ap-chengdu&quot;,\ntencentcloud_uin: &quot;...&quot;`, `2112296017722825700`)\"\n            >\n              <div\n                class=\"gatsby-code-button\"\n                data-tooltip=\"\"\n              >\n                复制代码<svg class=\"gatsby-code-button-icon\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\"><path fill=\"none\" d=\"M0 0h24v24H0V0z\"/><path d=\"M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z\"/></svg>\n              </div>\n            </div>\n<div class=\"gatsby-highlight\" data-language=\"javascript\"><pre class=\"language-javascript\"><code class=\"language-javascript\">callbackWaitsForEmptyEventLoop<span class=\"token punctuation\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span>\ngetRemainingTimeInMillis<span class=\"token punctuation\">:</span> <span class=\"token number\">200</span><span class=\"token punctuation\">,</span>\nmemory_limit_in_mb<span class=\"token punctuation\">:</span> <span class=\"token number\">128</span><span class=\"token punctuation\">,</span>\ntime_limit_in_ms<span class=\"token punctuation\">:</span> <span class=\"token number\">3000</span><span class=\"token punctuation\">,</span>\nenvironment<span class=\"token punctuation\">:</span> <span class=\"token string\">\"{\"</span><span class=\"token constant\">SCF_NAMESPACE</span><span class=\"token string\">\":\"</span>demo<span class=\"token string\">\",\"</span><span class=\"token constant\">TENCENTCLOUD_SECRETID</span><span class=\"token string\">\":\"</span><span class=\"token operator\">...</span><span class=\"token string\">\",\"</span><span class=\"token constant\">TENCENTCLOUD_SECRETKEY</span><span class=\"token string\">\":\"</span><span class=\"token operator\">...</span><span class=\"token string\">\",\"</span><span class=\"token constant\">TENCENTCLOUD_SESSIONTOKEN</span><span class=\"token string\">\":\"</span><span class=\"token operator\">...</span><span class=\"token string\">\"}\"</span>\nfunction_name<span class=\"token punctuation\">:</span> <span class=\"token string\">\"params\"</span><span class=\"token punctuation\">,</span>\nfunction_version<span class=\"token punctuation\">:</span> <span class=\"token string\">\"$LATEST\"</span><span class=\"token punctuation\">,</span>\nnamespace<span class=\"token punctuation\">:</span> <span class=\"token string\">\"demo\"</span><span class=\"token punctuation\">,</span>\nrequest_id<span class=\"token punctuation\">:</span> <span class=\"token string\">\"ab42b693-8bfd-4dc1-b228-60360a63e06c\"</span><span class=\"token punctuation\">,</span>\ntencentcloud_appid<span class=\"token punctuation\">:</span> <span class=\"token string\">\"...\"</span><span class=\"token punctuation\">,</span>\ntencentcloud_region<span class=\"token punctuation\">:</span> <span class=\"token string\">\"ap-chengdu\"</span><span class=\"token punctuation\">,</span>\ntencentcloud_uin<span class=\"token punctuation\">:</span> <span class=\"token string\">\"...\"</span></code></pre></div>\n<p>从上面的内容可以看到，该对象包含的内容有：</p>\n<ul>\n<li>函数配置信息，比如设置的内容大小，超时时间等</li>\n<li>执行身份认证信息。如果设置了函数的<strong>运行角色</strong>（角色必须要包含对应操作的授权策略），在环境变量中会注入 secretId，secretKey，sessionToken，在访问第三方云服务，比如 cos、自定义监控数据上报时就可以使用这几个值直接调用云 api，而不用在代码里面去 hard code 各种密钥信息</li>\n<li>环境变量：包括了用户自定义的环境变量以及一些系统环境变量</li>\n<li>执行环境基本信息：包括了当前函数调用的地域，用户的 appId，uin</li>\n</ul>\n<h3 id=\"第三个参数-callback-是一个可选参数，在非异步函数中返回执行结果\"><a href=\"#%E7%AC%AC%E4%B8%89%E4%B8%AA%E5%8F%82%E6%95%B0-callback-%E6%98%AF%E4%B8%80%E4%B8%AA%E5%8F%AF%E9%80%89%E5%8F%82%E6%95%B0%EF%BC%8C%E5%9C%A8%E9%9D%9E%E5%BC%82%E6%AD%A5%E5%87%BD%E6%95%B0%E4%B8%AD%E8%BF%94%E5%9B%9E%E6%89%A7%E8%A1%8C%E7%BB%93%E6%9E%9C\" aria-label=\"第三个参数 callback 是一个可选参数，在非异步函数中返回执行结果 permalink\" class=\"anchor\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>第三个参数 callback 是一个可选参数，在非异步函数中返回执行结果</h3>\n<p>回调函数采用两个参数：一个 Error 和一个返回。返回对象必须与 <code class=\"language-text\">JSON.stringify</code> 兼容。异步函数将忽略 callback 的返回，必须通过 return、throw exception 或者 promise 来处理返回或错误</p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"86882909659537560000\"\n              data-toaster-class=\"gatsby-code-button-toaster\"\n              data-toaster-text-class=\"gatsby-code-button-toaster-text\"\n              data-toaster-text=\"代码复制成功\"\n              data-toaster-duration=\"3500\"\n              onClick=\"copyToClipboard(`const https = require('https')\nlet url = &quot;https://cloud.tencent.com/&quot;\n\nexports.main_handler = function(event, context, callback) {\n  https.get(url, (res) => {\n    callback(null, res.statusCode)\n  }).on('error', (e) => {\n    callback(Error(e))\n  })\n}`, `86882909659537560000`)\"\n            >\n              <div\n                class=\"gatsby-code-button\"\n                data-tooltip=\"\"\n              >\n                复制代码<svg class=\"gatsby-code-button-icon\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\"><path fill=\"none\" d=\"M0 0h24v24H0V0z\"/><path d=\"M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z\"/></svg>\n              </div>\n            </div>\n<div class=\"gatsby-highlight\" data-language=\"javascript\"><pre class=\"language-javascript\"><code class=\"language-javascript\"><span class=\"token keyword\">const</span> https <span class=\"token operator\">=</span> <span class=\"token function\">require</span><span class=\"token punctuation\">(</span><span class=\"token string\">'https'</span><span class=\"token punctuation\">)</span>\n<span class=\"token keyword\">let</span> url <span class=\"token operator\">=</span> <span class=\"token string\">\"https://cloud.tencent.com/\"</span>\n\nexports<span class=\"token punctuation\">.</span><span class=\"token function-variable function\">main_handler</span> <span class=\"token operator\">=</span> <span class=\"token keyword\">function</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">event<span class=\"token punctuation\">,</span> context<span class=\"token punctuation\">,</span> callback</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  https<span class=\"token punctuation\">.</span><span class=\"token function\">get</span><span class=\"token punctuation\">(</span>url<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">res</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n    <span class=\"token function\">callback</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span> res<span class=\"token punctuation\">.</span>statusCode<span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">on</span><span class=\"token punctuation\">(</span><span class=\"token string\">'error'</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">e</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n    <span class=\"token function\">callback</span><span class=\"token punctuation\">(</span><span class=\"token function\">Error</span><span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">}</span></code></pre></div>\n<h2 id=\"函数返回\"><a href=\"#%E5%87%BD%E6%95%B0%E8%BF%94%E5%9B%9E\" aria-label=\"函数返回 permalink\" class=\"anchor\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>函数返回</h2>\n<p>我们来看一下，针对异步场景（async 函数）和非异步场景，云函数怎么把返回值传递出去</p>\n<h3 id=\"异步函数\"><a href=\"#%E5%BC%82%E6%AD%A5%E5%87%BD%E6%95%B0\" aria-label=\"异步函数 permalink\" class=\"anchor\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>异步函数</h3>\n<p>对于异步函数，可以使用 return 和 throw 来发送返回或错误。函数必须使用 async 关键字。在异步函数中，第三个参数 callback 没有定义</p>\n<p>示例：异步函数</p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"11176975998930815000\"\n              data-toaster-class=\"gatsby-code-button-toaster\"\n              data-toaster-text-class=\"gatsby-code-button-toaster-text\"\n              data-toaster-text=\"代码复制成功\"\n              data-toaster-duration=\"3500\"\n              onClick=\"copyToClipboard(`const https = require('https')\nlet url = &quot;https://cloud.tencent.com/&quot;\n\nconst httpRequest = url => {\n  const promise = new Promise(function(resolve, reject) {\n    https\n      .get(url, res => {\n        resolve(res.statusCode)\n      })\n      .on('error', e => {\n        reject(Error(e))\n      })\n  })\n  return promise\n}\n\nexports.handler =  async function(event, context) {\n  try{\n    const result = await httpRequest(url)\n    // 在async函数中callback未定义\n    // callback(null, result)\n    return result\n  }catch(e) {\n    throw e\n  }\n}`, `11176975998930815000`)\"\n            >\n              <div\n                class=\"gatsby-code-button\"\n                data-tooltip=\"\"\n              >\n                复制代码<svg class=\"gatsby-code-button-icon\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\"><path fill=\"none\" d=\"M0 0h24v24H0V0z\"/><path d=\"M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z\"/></svg>\n              </div>\n            </div>\n<div class=\"gatsby-highlight\" data-language=\"javascript\"><pre class=\"language-javascript\"><code class=\"language-javascript\"><span class=\"token keyword\">const</span> https <span class=\"token operator\">=</span> <span class=\"token function\">require</span><span class=\"token punctuation\">(</span><span class=\"token string\">'https'</span><span class=\"token punctuation\">)</span>\n<span class=\"token keyword\">let</span> url <span class=\"token operator\">=</span> <span class=\"token string\">\"https://cloud.tencent.com/\"</span>\n\n<span class=\"token keyword\">const</span> <span class=\"token function-variable function\">httpRequest</span> <span class=\"token operator\">=</span> <span class=\"token parameter\">url</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> promise <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Promise</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">function</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">resolve<span class=\"token punctuation\">,</span> reject</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    https\n      <span class=\"token punctuation\">.</span><span class=\"token function\">get</span><span class=\"token punctuation\">(</span>url<span class=\"token punctuation\">,</span> <span class=\"token parameter\">res</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n        <span class=\"token function\">resolve</span><span class=\"token punctuation\">(</span>res<span class=\"token punctuation\">.</span>statusCode<span class=\"token punctuation\">)</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n      <span class=\"token punctuation\">.</span><span class=\"token function\">on</span><span class=\"token punctuation\">(</span><span class=\"token string\">'error'</span><span class=\"token punctuation\">,</span> <span class=\"token parameter\">e</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n        <span class=\"token function\">reject</span><span class=\"token punctuation\">(</span><span class=\"token function\">Error</span><span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n  <span class=\"token keyword\">return</span> promise\n<span class=\"token punctuation\">}</span>\n\nexports<span class=\"token punctuation\">.</span><span class=\"token function-variable function\">handler</span> <span class=\"token operator\">=</span>  <span class=\"token keyword\">async</span> <span class=\"token keyword\">function</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">event<span class=\"token punctuation\">,</span> context</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">try</span><span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">const</span> result <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> <span class=\"token function\">httpRequest</span><span class=\"token punctuation\">(</span>url<span class=\"token punctuation\">)</span>\n    <span class=\"token comment\">// 在async函数中callback未定义</span>\n    <span class=\"token comment\">// callback(null, result)</span>\n    <span class=\"token keyword\">return</span> result\n  <span class=\"token punctuation\">}</span><span class=\"token keyword\">catch</span><span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">throw</span> e\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span></code></pre></div>\n<h3 id=\"同步函数\"><a href=\"#%E5%90%8C%E6%AD%A5%E5%87%BD%E6%95%B0\" aria-label=\"同步函数 permalink\" class=\"anchor\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>同步函数</h3>\n<p>还是上面的例子，发起一个 http 请求，如果用同步函数实现，参照以下示例</p>\n<p>示例：同步函数，callback 返回</p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"18244431053465182000\"\n              data-toaster-class=\"gatsby-code-button-toaster\"\n              data-toaster-text-class=\"gatsby-code-button-toaster-text\"\n              data-toaster-text=\"代码复制成功\"\n              data-toaster-duration=\"3500\"\n              onClick=\"copyToClipboard(`const https = require('https')\nlet url = &quot;https://cloud.tencent.com/&quot;\n\nexports.handler = function(event, context, callback) {\n  https.get(url, (res) => {\n    // 只能通过callback返回，return会被忽略\n    callback(null, res.statusCode)\n  }).on('error', (e) => {\n    callback(Error(e))\n  })\n}`, `18244431053465182000`)\"\n            >\n              <div\n                class=\"gatsby-code-button\"\n                data-tooltip=\"\"\n              >\n                复制代码<svg class=\"gatsby-code-button-icon\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\"><path fill=\"none\" d=\"M0 0h24v24H0V0z\"/><path d=\"M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z\"/></svg>\n              </div>\n            </div>\n<div class=\"gatsby-highlight\" data-language=\"javascript\"><pre class=\"language-javascript\"><code class=\"language-javascript\"><span class=\"token keyword\">const</span> https <span class=\"token operator\">=</span> <span class=\"token function\">require</span><span class=\"token punctuation\">(</span><span class=\"token string\">'https'</span><span class=\"token punctuation\">)</span>\n<span class=\"token keyword\">let</span> url <span class=\"token operator\">=</span> <span class=\"token string\">\"https://cloud.tencent.com/\"</span>\n\nexports<span class=\"token punctuation\">.</span><span class=\"token function-variable function\">handler</span> <span class=\"token operator\">=</span> <span class=\"token keyword\">function</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">event<span class=\"token punctuation\">,</span> context<span class=\"token punctuation\">,</span> callback</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  https<span class=\"token punctuation\">.</span><span class=\"token function\">get</span><span class=\"token punctuation\">(</span>url<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">res</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">// 只能通过callback返回，return会被忽略</span>\n    <span class=\"token function\">callback</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span> res<span class=\"token punctuation\">.</span>statusCode<span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">on</span><span class=\"token punctuation\">(</span><span class=\"token string\">'error'</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">e</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n    <span class=\"token function\">callback</span><span class=\"token punctuation\">(</span><span class=\"token function\">Error</span><span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">}</span></code></pre></div>\n<h3 id=\"返回的时机\"><a href=\"#%E8%BF%94%E5%9B%9E%E7%9A%84%E6%97%B6%E6%9C%BA\" aria-label=\"返回的时机 permalink\" class=\"anchor\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>返回的时机</h3>\n<p>正常的 Node.js web framework 在 response 返回后，异步逻辑还是继续在执行的。而 Serverless 场景下，由于机制和 framework的差别，对于已经返回 responese 的情况，一种是等着异步都处理完再来返回，这样保证了一次调用的完整性。另外一种就是在返回后就直接结束当次调用，直接挂起异步处理。</p>\n<p>腾讯云云函数针对 Node.js 的异步场景，实现了<strong>返回和结束分离</strong>的特殊机制。</p>\n<p><img src=\"https://img.serverlesscloud.cn/2020518/1589773269819-7B57B0C624042275E5113E8BE1DCB305.jpg\"></p>\n<ul>\n<li>入口函数的同步执行过程完成及返回后，云函数的调用将立刻返回，并将代码的返回信息返回给函数调用方</li>\n<li>同步流程处理并返回后，代码中的异步逻辑可以继续执行和处理，直到异步事件执行完成后，云函数的实际执行过程才完成和退出。</li>\n</ul>\n<p>默认情况下，函数执行会等待所有异步执行结束才算一次调用结束，但也给用户提供了关闭事件循环等待的选项，用户可以关闭事件循环等待来自行控制函数的返回时机。通过在 callback 回调执行前设置 <code class=\"language-text\">context.callbackWaitsForEmptyEventLoop = false</code>，可以使云函数在执行返回后立刻冻结进程，不再等待异步循环内的事件</p>\n<p>比如一下示例代码，代码里面发起了一个异步 http 请求，另外有一个 2s 后执行的 setTimeout</p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"69109614256747640000\"\n              data-toaster-class=\"gatsby-code-button-toaster\"\n              data-toaster-text-class=\"gatsby-code-button-toaster-text\"\n              data-toaster-text=\"代码复制成功\"\n              data-toaster-duration=\"3500\"\n              onClick=\"copyToClipboard(`const https = require('https')\nlet url = &quot;https://cloud.tencent.com/&quot;\n\nconst httpRequest = url => {\n  const promise = new Promise(function(resolve, reject) {\n    https\n      .get(url, res => {\n        resolve(res.statusCode)\n      })\n      .on('error', e => {\n        reject(Error(e))\n      })\n  })\n  return promise\n}\n\nexports.main_handler =  async function(event, context) {\n  // 设置该选项为false会不等待异步队列执行完，而是在返回后直接冻结process\n  //context.callbackWaitsForEmptyEventLoop = false\n  try{\n    const result = await httpRequest(url)\n    setTimeout(() => {\n        console.log('timeout log')\n    }, 2000)\n    return result\n  }catch(e) {\n    throw e\n  }\n}`, `69109614256747640000`)\"\n            >\n              <div\n                class=\"gatsby-code-button\"\n                data-tooltip=\"\"\n              >\n                复制代码<svg class=\"gatsby-code-button-icon\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\"><path fill=\"none\" d=\"M0 0h24v24H0V0z\"/><path d=\"M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z\"/></svg>\n              </div>\n            </div>\n<div class=\"gatsby-highlight\" data-language=\"javascript\"><pre class=\"language-javascript\"><code class=\"language-javascript\"><span class=\"token keyword\">const</span> https <span class=\"token operator\">=</span> <span class=\"token function\">require</span><span class=\"token punctuation\">(</span><span class=\"token string\">'https'</span><span class=\"token punctuation\">)</span>\n<span class=\"token keyword\">let</span> url <span class=\"token operator\">=</span> <span class=\"token string\">\"https://cloud.tencent.com/\"</span>\n\n<span class=\"token keyword\">const</span> <span class=\"token function-variable function\">httpRequest</span> <span class=\"token operator\">=</span> <span class=\"token parameter\">url</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> promise <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Promise</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">function</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">resolve<span class=\"token punctuation\">,</span> reject</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    https\n      <span class=\"token punctuation\">.</span><span class=\"token function\">get</span><span class=\"token punctuation\">(</span>url<span class=\"token punctuation\">,</span> <span class=\"token parameter\">res</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n        <span class=\"token function\">resolve</span><span class=\"token punctuation\">(</span>res<span class=\"token punctuation\">.</span>statusCode<span class=\"token punctuation\">)</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n      <span class=\"token punctuation\">.</span><span class=\"token function\">on</span><span class=\"token punctuation\">(</span><span class=\"token string\">'error'</span><span class=\"token punctuation\">,</span> <span class=\"token parameter\">e</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n        <span class=\"token function\">reject</span><span class=\"token punctuation\">(</span><span class=\"token function\">Error</span><span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n  <span class=\"token keyword\">return</span> promise\n<span class=\"token punctuation\">}</span>\n\nexports<span class=\"token punctuation\">.</span><span class=\"token function-variable function\">main_handler</span> <span class=\"token operator\">=</span>  <span class=\"token keyword\">async</span> <span class=\"token keyword\">function</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">event<span class=\"token punctuation\">,</span> context</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token comment\">// 设置该选项为false会不等待异步队列执行完，而是在返回后直接冻结process</span>\n  <span class=\"token comment\">//context.callbackWaitsForEmptyEventLoop = false</span>\n  <span class=\"token keyword\">try</span><span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">const</span> result <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> <span class=\"token function\">httpRequest</span><span class=\"token punctuation\">(</span>url<span class=\"token punctuation\">)</span>\n    <span class=\"token function\">setTimeout</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n        console<span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span><span class=\"token string\">'timeout log'</span><span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token number\">2000</span><span class=\"token punctuation\">)</span>\n    <span class=\"token keyword\">return</span> result\n  <span class=\"token punctuation\">}</span><span class=\"token keyword\">catch</span><span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">throw</span> e\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span></code></pre></div>\n<p>在 http 请求完成后，会立即返回给调用方，不会等待 setTimeout 的异步实践执行完。而在返回后，程序会继续执行，直到 setTimeout 的事件执行完才算本次调用结束。</p>\n<p>在设置了 <code class=\"language-text\">context.callbackWaitsForEmptyEventLoop = false</code> 后，在 return 后进程会被冻结，setTimeout 里面的执行逻辑会被挂起</p>\n<h2 id=\"完整流程图\"><a href=\"#%E5%AE%8C%E6%95%B4%E6%B5%81%E7%A8%8B%E5%9B%BE\" aria-label=\"完整流程图 permalink\" class=\"anchor\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>完整流程图</h2>\n<p>以下是单实例内 runtime 运行的完整流程图</p>\n<p><img src=\"https://img.serverlesscloud.cn/2020518/1589773295474-E490E0A9A112F9E5B0F54C2421D891E8%E5%89%AF%E6%9C%AC.jpg\" alt=\"流程图\"></p>\n<p>针对 Node.js应用，有以下几个实践建议：</p>\n<ul>\n<li>日志：runtime重写了 console 的几个主要方法，而且是在 require 用户文件之后，所以用户自定义日志选项会无效</li>\n<li>缓存复用：在入口函数外可以定义变量，存储可以复用的缓存对象，比如数据库的连接等\nNode.js 的模块实现逻辑中，如果一个 module 被 require 过，该模块就会被 cache 到内存中，再次被 require时不会重新初始化。针对这一特性，如果实例一直再复用，那么在入口文件中，入口函数外定义的变量都不会被销毁，可以达到复用的效果</li>\n<li>内置部分 npm 包，可以直接使用，具体参照文档。部署云函数代码时推荐 <code class=\"language-text\">npm install --production</code>，减少代码包的体积，提升上传速度和执行速度</li>\n<li>执行角色：配置执行角色，从 context 中可以获取临时的密钥信息，可以用了访问有相应权限的第三方服务，而不用在代码内写死密钥信息</li>\n</ul>\n<blockquote>\n<p>回放：点击观看 <a href=\"https://cloud.tencent.com/edu/learning/live-2437\">Tencent Serverless Hours 线上分享会第一期</a></p>\n</blockquote>\n<hr>\n<div id='scf-deploy-iframe-or-md'></div>\n<hr>\n<blockquote>\n<p><strong>传送门：</strong></p>\n<ul>\n<li>GitHub: <a href=\"https://github.com/serverless/serverless/blob/master/README_CN.md\">github.com/serverless</a></li>\n<li>官网：<a href=\"https://serverless.com/\">serverless.com</a></li>\n</ul>\n</blockquote>\n<p>欢迎访问：<a href=\"https://serverlesscloud.cn/\">Serverless 中文网</a>，您可以在 <a href=\"https://serverlesscloud.cn/best-practice\">最佳实践</a> 里体验更多关于 Serverless 应用的开发！</p>","tableOfContents":"<ul>\n<li>\n<p><a href=\"/blog/2020-05-12-nodejs-runtime/#%E5%85%A5%E5%8F%A3%E5%87%BD%E6%95%B0%E7%9A%84%E5%8F%82%E6%95%B0\">入口函数的参数</a></p>\n<ul>\n<li><a href=\"/blog/2020-05-12-nodejs-runtime/#%E7%AC%AC%E4%B8%80%E4%B8%AA%E5%8F%82%E6%95%B0%E6%98%AF-event%EF%BC%8C%E7%94%A8%E6%9D%A5%E4%BC%A0%E9%80%92%E8%A7%A6%E5%8F%91%E4%BA%8B%E4%BB%B6%E6%95%B0%E6%8D%AE\">第一个参数是 event，用来传递触发事件数据</a></li>\n<li><a href=\"/blog/2020-05-12-nodejs-runtime/#%E7%AC%AC%E4%BA%8C%E4%B8%AA%E5%8F%82%E6%95%B0-context%EF%BC%8C%E5%87%BD%E6%95%B0%E8%BF%90%E8%A1%8C%E6%97%B6%E4%BF%A1%E6%81%AF\">第二个参数 context，函数运行时信息</a></li>\n<li><a href=\"/blog/2020-05-12-nodejs-runtime/#%E7%AC%AC%E4%B8%89%E4%B8%AA%E5%8F%82%E6%95%B0-callback-%E6%98%AF%E4%B8%80%E4%B8%AA%E5%8F%AF%E9%80%89%E5%8F%82%E6%95%B0%EF%BC%8C%E5%9C%A8%E9%9D%9E%E5%BC%82%E6%AD%A5%E5%87%BD%E6%95%B0%E4%B8%AD%E8%BF%94%E5%9B%9E%E6%89%A7%E8%A1%8C%E7%BB%93%E6%9E%9C\">第三个参数 callback 是一个可选参数，在非异步函数中返回执行结果</a></li>\n</ul>\n</li>\n<li>\n<p><a href=\"/blog/2020-05-12-nodejs-runtime/#%E5%87%BD%E6%95%B0%E8%BF%94%E5%9B%9E\">函数返回</a></p>\n<ul>\n<li><a href=\"/blog/2020-05-12-nodejs-runtime/#%E5%BC%82%E6%AD%A5%E5%87%BD%E6%95%B0\">异步函数</a></li>\n<li><a href=\"/blog/2020-05-12-nodejs-runtime/#%E5%90%8C%E6%AD%A5%E5%87%BD%E6%95%B0\">同步函数</a></li>\n<li><a href=\"/blog/2020-05-12-nodejs-runtime/#%E8%BF%94%E5%9B%9E%E7%9A%84%E6%97%B6%E6%9C%BA\">返回的时机</a></li>\n</ul>\n</li>\n<li><a href=\"/blog/2020-05-12-nodejs-runtime/#%E5%AE%8C%E6%95%B4%E6%B5%81%E7%A8%8B%E5%9B%BE\">完整流程图</a></li>\n</ul>"},"previousBlog":{"id":"7cdbd3fc-5cc1-50eb-a9ac-21fb5de1cfeb","frontmatter":{"thumbnail":"https://img.serverlesscloud.cn/2020512/1589268598969-haibao.jpg","authors":["Serverless 社区"],"categories":["news"],"date":"2020-05-12T00:00:00.000Z","title":"大咖集结 | 首届 ServerlessDays · China 嘉宾阵容曝光","description":"大会不仅有 ServerlessDays 组织者 Ant Stanley、Serverless 白皮书作者 Johann Schleier-Smith 等全球顶级意见领袖分享 Serverless 前瞻，也有 Google、IBM、腾讯云等国内外一线厂商带来 Serverless 技术新风向，更有 BBC、滴滴、爱奇艺等企业的最佳实践指导！","authorslink":["https://zhuanlan.zhihu.com/ServerlessGo"],"translators":null,"translatorslink":null,"tags":["ServerlessDays","Meetup"],"keywords":"Serverless,ServerlessDays,ServerlessChina","outdated":null},"wordCount":{"words":84,"sentences":13,"paragraphs":13},"fileAbsolutePath":"/opt/build/repo/content/blog/2020-05-12-serverlessdays-poster.md","fields":{"slug":"/blog/2020-05-12-serverlessdays-poster/","keywords":["serverless","云原生","Serverless","ServerlessDays","serverless","serverlesscloud","技术"]}},"nextBlog":{"id":"13c6d4ce-80d3-50b2-8670-125074e036ee","frontmatter":{"thumbnail":"https://img.serverlesscloud.cn/2020514/1589461771804-layer.jpg","authors":["Alfred"],"categories":["guides-and-tutorials"],"date":"2020-05-11T00:00:00.000Z","title":"如何借助 Layer 实现云函数快速打包轻松部署","description":"层的功能为依赖库和不经常修改的静态文件提供了新的存储方案，与函数的剥离使得这类文件能够多函数复用，版本化管理；随着层功能的发展，腾讯云 Serverless team 也将进一步拓展层功能的使用","authorslink":["https://zhuanlan.zhihu.com/ServerlessGo"],"translators":null,"translatorslink":null,"tags":["Serverless","layer"],"keywords":"Serverless 全局变量组件,Serverless 单独部署组件,Serverless Component","outdated":null},"wordCount":{"words":286,"sentences":34,"paragraphs":34},"fileAbsolutePath":"/opt/build/repo/content/blog/2020-05-11-scf-layer-practice.md","fields":{"slug":"/blog/2020-05-11-scf-layer-practice/","keywords":["python","serverless","云函数","函数","绑定","cmq","requests","依赖","代码"]}},"recommendBlogs":{"edges":[{"node":{"id":"665f9ce2-4451-59fd-bf98-1861789d3b3b","frontmatter":{"thumbnail":"https://s3-us-west-2.amazonaws.com/assets.blog.serverless.com/Serverless_logo.png","authors":["AndreaPasswater"],"categories":["guides-and-tutorials","engineering-culture"],"date":"2018-03-19T00:00:00.000Z","title":"如何为无服务器开放源代码项目做贡献","description":"想要为无服务器开放源代码项目做贡献？您可以遵循下面的指南。","authorslink":null,"translators":null,"translatorslink":null,"tags":null,"keywords":null,"outdated":null},"wordCount":{"words":96,"sentences":36,"paragraphs":36},"fileAbsolutePath":"/opt/build/repo/content/blog/2018-03-19-how-contribute-to-serverless-open-source.md","fields":{"slug":"/blog/2018-03-19-how-contribute-to-serverless-open-source/","keywords":["serverless","无服务器","serverless","github","插件","服务器","贡献","示例","blog","contribute"]}}},{"node":{"id":"a3e92579-65c3-5159-937c-32d18c5df7d7","frontmatter":{"thumbnail":"https://s3-us-west-2.amazonaws.com/assets.blog.serverless.com/why-not/why-not-header.png","authors":["AndreaPasswater"],"categories":["guides-and-tutorials","operations-and-observability","engineering-culture"],"date":"2018-03-21T00:00:00.000Z","title":"不适合选择无服务器的情境及原因","description":"无服务器既有优点也有缺点。那么，哪些情境下不适合选择无服务器？原因又是什么呢？","authorslink":null,"translators":null,"translatorslink":null,"tags":null,"keywords":null,"outdated":null},"wordCount":{"words":163,"sentences":43,"paragraphs":43},"fileAbsolutePath":"/opt/build/repo/content/blog/2018-03-21-when-why-not-use-serverless.md","fields":{"slug":"/blog/2018-03-21-when-why-not-use-serverless/","keywords":["faas","react","serverless","spa","无服务器","无服务器函数","无服务器架构","无服务器开发","服务器","twitter","serverless","blockquote","lang","script","en"]}}},{"node":{"id":"6a16520b-7886-582e-9182-64e50712d486","frontmatter":{"thumbnail":"https://s3-us-west-2.amazonaws.com/assets.blog.serverless.com/vendor+choice/serverless-data-portability.jpg","authors":["NickGottlieb"],"categories":["engineering-culture","guides-and-tutorials"],"date":"2018-06-20T00:00:00.000Z","title":"浅谈无服务器、数据锁定和供应商选择","description":"供应商选择是如今 IT 领导者需要考虑的最重要事项，而这一点可利用数据可移植性来实现。","authorslink":null,"translators":null,"translatorslink":null,"tags":null,"keywords":null,"outdated":null},"wordCount":{"words":205,"sentences":33,"paragraphs":33},"fileAbsolutePath":"/opt/build/repo/content/blog/2018-06-20-data-lockin-vendor-choice-portability.md","fields":{"slug":"/blog/2018-06-20-data-lockin-vendor-choice-portability/","keywords":["go","serverless","无服务器","无服务器架构","供应商","serverless","开发人员","数据","锁定","选择","服务"]}}},{"node":{"id":"94741abb-10ba-5db1-9756-cd1d573473fa","frontmatter":{"thumbnail":"https://s3-us-west-2.amazonaws.com/assets.blog.serverless.com/webstorm-ide/streamline-webstorm-serverless2.jpg","authors":["EslamHefnawy"],"categories":["guides-and-tutorials","engineering-culture"],"date":"2018-08-15T00:00:00.000Z","title":"如何使用 WebStorm 简化无服务器工作流程","description":"在本文中，我将和您分享如何使用 WebStorm 进行无服务器特定的 IDE 设置以及如何利用它来极大地加快无服务器工作流程。","authorslink":null,"translators":null,"translatorslink":null,"tags":null,"keywords":null,"outdated":null},"wordCount":{"words":234,"sentences":54,"paragraphs":54},"fileAbsolutePath":"/opt/build/repo/content/blog/2018-08-15-streamline-serverless-workflow-webstorm.md","fields":{"slug":"/blog/2018-08-15-streamline-serverless-workflow-webstorm/","keywords":["nodejs","serverless","无服务器","无服务器开发","serverless","WebStorm","webstorm","服务器","blog","assets"]}}},{"node":{"id":"713a0563-4bf9-5721-bacb-3b4ef609fe4a","frontmatter":{"thumbnail":"https://s3-us-west-2.amazonaws.com/assets.blog.serverless.com/camp-fire/camp-fire-housing-thumb.jpg","authors":["EricWyne"],"categories":["guides-and-tutorials","user-stories"],"date":"2018-12-05T00:00:00.000Z","title":"Serverless Twitter 机器人帮助为坎普山火受灾者安置住房","description":"加利福尼亚州的坎普山火致使数千人流离失所，为此，我构建了一个简单的 Serverless Twitter 机器人来帮助将受灾者安置在临时住房！","authorslink":["https://serverless.com/author/ericwyne/"],"translators":["Aceyclee"],"translatorslink":["zhihu.com/people/Aceyclee"],"tags":null,"keywords":null,"outdated":null},"wordCount":{"words":157,"sentences":26,"paragraphs":26},"fileAbsolutePath":"/opt/build/repo/content/blog/2018-12-05-serverless-twitter-camp-fire.md","fields":{"slug":"/blog/2018-12-05-serverless-twitter-camp-fire/","keywords":["serverless","无服务器","云函数","Serverless","org","住房","Twitter","函数","受灾","机器人","山火"]}}},{"node":{"id":"17c972d9-0583-51f6-9d5d-c2ba5f21b6a3","frontmatter":{"thumbnail":"https://img.serverlesscloud.cn/20191227/1577409288454-v2-577c2b21d600e3ea07f156f3e9d2d6b8_1200x500.jpg","authors":["Alfred Huang"],"categories":["guides-and-tutorials"],"date":"2019-08-21T00:00:00.000Z","title":"Serverless 的运行原理与组件架构","description":"本文重点探讨下开发者使用 Serverless 时经常遇到的一些问题，以及如何解决","authorslink":["https://zhuanlan.zhihu.com/ServerlessGo"],"translators":null,"translatorslink":null,"tags":["运行原理","serverless"],"keywords":"Serverless 运行原理,Serverless 组件架构","outdated":null},"wordCount":{"words":236,"sentences":33,"paragraphs":33},"fileAbsolutePath":"/opt/build/repo/content/blog/2019-08-21-serverless-operation-architecture.md","fields":{"slug":"/blog/2019-08-21-serverless-operation-architecture/","keywords":["koa","serverless","云函数","Serverless","用户","函数","请求","实例","形态","业务","serverlesscloud"]}}},{"node":{"id":"ae4fd2f8-515c-5aec-b584-38427ef33f7e","frontmatter":{"thumbnail":"https://img.serverlesscloud.cn/2020114/1578989800047-part-00492-780.jpg","authors":["Anycodes"],"categories":["guides-and-tutorials","user-stories"],"date":"2019-09-16T00:00:00.000Z","title":"突破传统 OJ 瓶颈，「判题姬」接入云函数","description":"通过 Serverless 实现在线编程","authorslink":["https://www.zhihu.com/people/liuyu-43-97"],"translators":null,"translatorslink":null,"tags":["在线编程","云函数"],"keywords":"Serverless 在线编程,Serverless OJ","outdated":null},"wordCount":{"words":169,"sentences":30,"paragraphs":30},"fileAbsolutePath":"/opt/build/repo/content/blog/2019-09-16-online-Judge.md","fields":{"slug":"/blog/2019-09-16-online-Judge/","keywords":["python","serverless","云函数","代码","函数","serverless"]}}},{"node":{"id":"545ab3d2-e14e-5cc2-8548-0e863eac942b","frontmatter":{"thumbnail":"https://s3-us-west-2.amazonaws.com/assets.blog.serverless.com/2019-10-deployment-best-practices/safeguard-header.png","authors":["FernandoMedinaCorey"],"categories":["guides-and-tutorials"],"date":"2019-10-14T00:00:00.000Z","title":"无服务器部署最佳实践","description":"了解部署无服务器应用时的一些最佳实践。","authorslink":null,"translators":null,"translatorslink":null,"tags":null,"keywords":null,"outdated":null},"wordCount":{"words":221,"sentences":46,"paragraphs":46},"fileAbsolutePath":"/opt/build/repo/content/blog/2019-10-14-serverless-deployment-best-practices.md","fields":{"slug":"/blog/2019-10-14-serverless-deployment-best-practices/","keywords":["serverless","无服务器","serverless","部署","服务器","开发人员","应用","安全措施","使用","函数"]}}}],"totalCount":31}},"pageContext":{"isCreatedByStatefulCreatePages":false,"blogId":"38503518-dfcb-58ff-8b5a-f545d5459e6b","previousBlogId":"7cdbd3fc-5cc1-50eb-a9ac-21fb5de1cfeb","nextBlogId":"13c6d4ce-80d3-50b2-8670-125074e036ee","categories":["guides-and-tutorials"]}}}