阿里云函数计算使用手札

NodeJS

String格式化 (变量放入字符串内)

Link

1
2
3
var my_name = 'John';
var s = `hello ${my_name}, how are you doing`;
console.log(s); // prints hello John, how are you doing

注意字符串需要使用 ` 符号包围

Promise chains | Async Await

这个涉及的问题很多,光看的Link就有10+. 总体来说需要分步骤学习这个. Promise -> Promise Chains -> Async Await

需要处理的问题

比如: 玩家的注册模块. 玩家传入 用户名(username),密码(password),激活码(cdkey).

后端需要分步骤校验

  • A: 用户名 正则校验 (是否有不能使用的特殊字符,位数是否在5~30之间等等)
  • B: 密码 正则校验
  • C: 激活码 正则校验
  • D: 检测 激活码是否可用 (异步)
  • E: 检测 用户名是否占用 (异步)
  • F-G: 把注册信息写入库 (异步)

原始方案

最原始的方案也就是使用 回调函数.

1
2
3
4
5
6
7
8
if(!A.RunLogic(username)) throw new LogicError();
if(!B.RunLogic(password)) throw new LogicError();
if(!C.RunLogic(cdkey)) throw new LogicError();

D.RunLogic(OnDLogicSucceed,OnDLogicInError)
function OnDLogicSucceed(){}
function OnDLogicInError(){}
...

当然一般JS都会使用匿名函数嵌套. 不过当异步逻辑很多且无法并行时候. 比如我这个case 那就需要嵌套许多层执行. 代码会看起来非常乱

Promise 和 Promise Chains

这东西被一堆人文章写的很玄幻. 其实也没那么复杂. 廖老师,阮老师,JavaScript Promise迷你书 这几个都是不错的参考,写的很全面.

我比较喜欢 廖老师 对 Promise的解释 承诺执行

换成我比较了解的东西来说 , Promise 和 Promise Chains 也就是 DoTween的TimeLine 😅

1
2
3
4
5
6
7
8
9
10
11
12
var p = Promise.resolve($入参);

p.then(A.RunLogic)
.then(B.RunLogic)
.then(C.RunLogic)
.then(D.RunLogic)
.then(E.RunLogic)
.then(F.RunLogic)
.then(G.RunLogic)
.catch(CommonErrorHandler);

function CommonErrorHandler(_e){}

用这种方式写完以后代码会非常简洁易懂. 不过我之前在这里也耽误了好多时间. 主要是每次运行时候都会报错

UnhandledPromiseRejectionWarning: Unhandled promise rejection
DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node. js process with a non-zero exit code.

究其原因是因为当时还是没对 承诺执行, TimeLine的方式理解透. 因为我的username,password,cdkey 都是 req里面直接解析出来的. 所以我当时代码写的是

1
2
3
4
p.then(A.RunLogic(username))
.then(B.RunLogic(password))
.then(C.RunLogic(cdkey))
...

也就是Callback,和Callback()之间的区别了. 翻译成TimeLine的语法就是 p的一系列then和catch都是在准备TimeLine. 后续需要调用 p.play();来真实的执行Timeline. 只不过js无需手动执行play方法,这部分被自动执行了.

同时这也就暴露出另外一个问题了,就是如何传参. Promise Chains中 要么参数是一个一个传递下去(A的出参就是B的入参),要么就放在外面 一个大家都能引用到的位置.

第一个方案开始觉得不现实,因为ABC的入参我是直接通过req得到的.而放在外面也有问题.因为我几个逻辑是拆分到不同的js文件,作为模块的方式给require进来的. 也就是他们的域是独立的.我还得把这个共享的arg在运行前 先通过init函数给传入到模块自己的域内.

所以后来想到的折中方案就是构建context对象,然后每个逻辑出参时候再把context给传出去

1
2
3
4
5
6
7
8
9
10
11
12
13
var context = {"u":"abcedf","p":"xxxx","cdkey":"xxxxx"}

var p = Promise.resolve(context);
p.then(A.RunLogic)
.then(B.RunLogic)
.then(C.RunLogic)
.then(D.RunLogic)
.then(E.RunLogic)
.then(F.RunLogic)
.then(G.RunLogic)
.catch(CommonErrorHandler);

function CommonErrorHandler(_e){}

Async Await

Link

这俩似乎就是为了解决我上面遇到的那个问题. 并且全部逻辑可以直接放在try.catch里面执行了. 这个类比unity里面的yield还是比较好理解的. 并且JS里面好像本来就有yield

Difference between async/await and ES6 yield with generators
The Difference Between Async/Await and Generators
Modern Javascript and Asynchronous Programming: Generators/Yield vs. Async/Await

反正JS is not my things,凑合能写就完了,我也就没深究. 总之用了 async 和 await 以后 代码就变得很好看了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
try{
//同步方法,检测失败直接抛异常
if(!A.RunLogic(username)) throw new LogicError();
if(!B.RunLogic(password)) throw new LogicError();
if(!C.RunLogic(cdkey)) throw new LogicError();

//异步方法
await D.RunLogic($入参);
await E.RunLogic($入参);
await F.RunLogic($入参);
...

}catch(_e)
{...}

exports的用法

Link: exports的用法:Node.js模块的接口设计模式

正则验证

Link

验证用户名的正则:

1
2
3
//首字母必须是字母或数组,后面可以出现下划线
var patrn = /^[a-zA-Z0-9]{1}([a-zA-Z0-9]|[_[email protected]]){2,30}$/;
if (!patrn.test(_username)) {throw new LogicError();}

验证密码的正则:

1
2
3
//6~30 可以有部分特殊字符
var patrn = /^([a-zA-Z0-9]|[_[email protected]#$%^&*]){1,30}$/;
if (!patrn.test(_password)){throw new LogicError();}

阿里云服务配置

无法Debug代码,提示端口被占用

VSCode进行HttpTrigger的Function Debug时候 第二次必提示错误:

(HTTP code 500) server error - driver failed programming external connectivity on endpoint fun_local_1567073065688_0wt83wl (9d70b304f168c38fbae746d9b58d146e7e67d396da145d8eb0f5b76ae60186cf): Bind for 0.0.0.0:3000 failed: port is already allocated

应该是我没正常返回导致的?或者是本来的Bug.

不够还好,直接命令行停到那个container就行了

首先用 docker container ls 列出所有的container,然后用 docker stop $containerID 停止对应的container

VSCode中配置Service的Log和Role

这个折腾了半天,之前发现后台的Log时有时无,一直找不到原因.原来是我每次上传本地函数后本地配置都会覆盖服务端的,然后本地的配置里面又没配置Log和Role

注意一下Role里面需要填的是那个 acs:ram::xxx不是后台配置的那个别称. 相应的可以在后台 https://ram.console.aliyun.com/overview里面找到.

坚持原创技术分享,您的支持将鼓励我继续创作!