nodejs开发

基础

npx的全称是Node Package Runner,是一个命令行工具,它可以在不安装包的情况下直接运行Node.js包中的命令。npx的作用类似于npm全局安装包,然后在命令行中执行安装包里的命令,但npx不需要提前安装,它会自动下载所需的包并执行命令。因此,npx被称为“npm 5.2.0+附带的一个功能”。
在 npx 命令后加上 ts-node 命令 tsx,表示使用 ts-node 解析并运行 TypeScript 文件。这种方式可以避免在本地全局安装 ts-node,而且方便在多个项目中使用不同版本的 ts-node。

在 TypeScript 中,?. 表示可选链操作符,它用于访问可能不存在的属性或方法。
在旧版的 JavaScript 中,当试图访问一个不存在的属性或方法时,程序会抛出一个类型为“undefined”的错误。在 TypeScript 中,可以使用可选链操作符?. 来避免这种错误,当访问的对象或属性不存在时,表达式会直接返回 undefined,而不会抛出错误。

异步js

XHR 有 5 种状态:XMLHttpRequest.readyState
|Value| State| Description|
|—–|—–|—–|
|0 |UNSENT |Client has been created. open() not called yet.|
|1 |OPENED |open() has been called.|
|2 |HEADERS_RECEIVED |send() has been called, and headers and status are available.|
|3 |LOADING |Downloading; responseText holds partial data.|
|4 |DONE |The operation is complete.|

在一个普通的请求中,如果正常完成,会返回 DONE,然后就可以根据 xhr 的状态码和 HTTP 的状态码来进行处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const getData = () => {
const xhr = new XMLHttpRequest();
xhr.addEventListener("readystatechange", () => {
if (xhr.readyState === xhr.DONE) {
if (xhr.status === 200) {
console.log(xhr.responseText);
} else {
console.log("error");
}
}
});
xhr.open("GET", "xxx");
xhr.send();
};

如果要通过回调函数的方式,则可以改为

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const getData = (callback) => {
const xhr = new XMLHttpRequest();
xhr.addEventListener("readystatechange", () => {
if (xhr.readyState === xhr.DONE) {
if (xhr.status === 200) {
callback(xhr.responseText, undefined);
} else {
callback(undefined, "error");
}
}
});
xhr.open("GET", "xxx");
xhr.send();
};

getData((data, err) => {
if (err) {
console.log(err);
} else {
console.log(data);
}
});

传入了一个回调函数,带两个参数,并进行成功与否的处理。这部分处理很像 Go 的双返回值,太像了

1
2
3
4
5
6
7
8
func getData() (string, error) {
return data, nil;
}

data, err := getData()
if err != nil {
// print data
}

如果在 getData() 前后分别打印 1 2,则会输出 12,再执行里面的耗时任务,达到一个异步处理的效果。

这就是回调函数,但是回调函数带来了一个问题。如果任务之间有强依赖,比如我要先登录,再获取用户信息,再进行另外的操作,如果用回调函数的写法,就会出现一个问题

1
2
3
4
5
6
7
8
9
10
11
12
13
login((data, err) => {
if (!err) {
getUserInfo((data, err) => {
if (!err) {
getData((data, err) => {
if (!err) {
// ok
}
});
}
});
}
});

如果有 N 个请求,就会出现一个 >,业界叫回调地狱,虽然代码是给机器执行的,但那是编译过的,源代码是给人看的,这样会不好维护,于是出现了一个新对象 Promise

Promise 会出现两种可能的结果:

  • 成功的时候,走 resolve
  • 失败则走 reject

这样上面的 getData() 就可以改写成

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const getData = () => {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.addEventListener("readystatechange", () => {
if (xhr.readyState === xhr.DONE) {
if (xhr.status === 200) {
resolve(xhr.responseText);
} else {
reject("error");
}
}
});
xhr.open("GET", "xxx");
xhr.send();
});
};

而 Promise 的后续一般用 then() 来处理 resolve 的结果,catch() 来处理 reject 的结果

1
2
3
getData()
.then((data) => console.log(data))
.catch((err) => console.log(err));

那么 Promise 是如何解决回调地狱的?

1
2
3
4
5
6
7
8
9
10
11
12
login()
.then((data) => {
console.log(data);
return getUserInfo();
})
.then((data) => {
console.log(data);
return getData();
})
.then((data) => {
console.log(data);
});

既然每个请求都返回 Promise,而 Promise 又要 then() 来处理,那么通过将 Promise 进行 return, 再通过 then() 来处理,则可完成相同的效果。这样在一定程度上避免的回调的嵌套,但是还是不太直观。

另外的方式?
通过将函数设置为 async,在这个异步函数内部则可通过 await 来进行顺序的处理。改写上面方法,要定义一个异步方法,async 关键字要写在 function 前面,如果是箭头函数,则在参数前面

1
2
3
4
5
6
7
8
9
10
11
12
const getData = async () => {
const loginResponse = await fetch("login");
const loginData = await loginResponse.json();

const userInfoResponse = await fetch("userInfo");
const userInfoData = await userInfoResponse.json();

const response = await fetch("data");
const data = response.json();

return data;
};

由于 fetch 本身是异步的,返回一个 Promise,因此里面通过 await 关键字可以得到最终的结果。又由于这个方法也设置成了异步方法,所以也需要通过 then() 再来进行最后的处理

1
getData().then((data) => console.log(data));

值得注意的是,一般情况下,请求不是 2xx 就会当作失败,出异常,理应走 reject,但是这个 fetch 即使出了 404 或者 500,还是走 resolve,这个时候,需要进一步判断它的 ok 属性或 HTTP 状态码,才能判断请求是否正常。

nephen wechat
欢迎您扫一扫上面的微信公众号,订阅我的博客!
坚持原创技术分享,您的支持将鼓励我继续创作!