由于我与Deno一起玩了一点,我注意到它们的服务器是使用异步峰值创建的,这意味着我们可以使用for...await...of
循环并简化了Web服务器的写作。
从那以后,我一直在挖掘,终于找到了一种使我们心爱的http服务器在node.js中成为异步的方法。
import http from "http"
const getBody = async request => {
let body = ""
for await (const chunk of request) {
body += chunk.toString()
}
return body
}
const createServer = (port, host) => {
const server = http.createServer()
server[Symbol.asyncIterator] = function() {
return {
next() {
return new Promise((resolve, reject) => {
const onRequest = (request, response) => {
resolve({
done: false,
value: { request, response }
})
server.off("request", onRequest)
}
server.on("request", onRequest)
})
}
}
}
return server
}
const server = createServer()
server.listen(8000, "0.0.0.0", () => {
console.log("Server listening")
})
for await (const {request, response} of server) {
if (request.method === "POST") {
const body = await getBody(request)
console.log(body)
}
response.end("OK")
}
迭代器
迭代器是一种允许for...of
循环和传播算子[...iterable]
之类的构造,可以在峰值对象上操作。
这也是为什么我们不能循环浏览一个对象的原因:对象不能定义一个Symbol.iterator
方法,该方法在运行时由for...of
循环检查,因此,如果我们尝试在普通对象上循环,则为什么要获得错误something is not iterable
。
字符串,数组和javaScript中的更多对象已经定义了返回迭代器协议的Symbol.iterator
方法:一个定义方法next
的对象,每次我们需要发送新迭代时称为next
。
异步迭代器
异步迭代器只是一个迭代器,它从承诺的结果中获得值。这意味着我们还可以寄回已久的值,并为我们的迭代器带来一个全新的可能性。
可以使对象变得很有能力
任何对象都可以成为自身的迭代版本,甚至数字(因为几乎所有内容都是JavaScript中的对象)。
这意味着像[...100]
这样的事物在不久的将来可能是可能的,因为Number
构造函数可以在其原型中包含Symbol.iterator
的方法。
当然,高度不鼓励增强JavaScript中本机对象的原始物质,但如果您想尝试迭代器,这是一个有趣的练习。
Number.prototype[Symbol.iterator] = function* () {
for (let number = 0; number < this; number++) {
yield number
}
}
console.log([...10])
// [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
再次,不要在生产应用中复制,因为增强本地物体的原型并可能在可能使用这些原型的库中造成严重的损害和冲突是非常危险的,即使语言正在使用类似的语言进行更新构造。
节点请求是异步迭代的
很长一段时间以来,我经常创建小助手来帮助我获得我的要求的身体,然后可以从流中翻译(这是节点平台的核心,但很难掌握的概念)承诺。
import http from "http"
const getBody = request => {
return new Promise((resolve, reject) => {
let body = ""
request.on("data", chunk => {
body += chunk.toString()
})
request.on("end", () => {
resolve(body)
})
request.on("error", error => {
reject(error)
})
})
}
http.createServer(async (request, response) => {
if (request.method === "POST") {
const body = await getBody()
console.log(body)
}
response.end("OK")
}).listen(8000, "0.0.0.0", () => {
console.log("Listening")
})
request
这是一个流,流streams实现了EventEmitter
类,它使我们能够在从HTTP流进行解码或发生错误时,可以收听事件。
这不是一项琐碎的任务,因为一开始操纵事件可能很难,尤其是对于初学者甚至已确认的开发人员。
我们也可以通过使用for...await...of
循环。
import http from "http"
const getBody = async request => {
let body = ""
for await (const chunk of request) {
body += chunk.toString()
}
return body
}
http.createServer(async (request, response) => {
if (request.method === "POST") {
const body = await getBody()
console.log(body)
}
response.end("OK")
}).listen(8000, "0.0.0.0", () => {
console.log("Listening")
})
这是因为request
实现了迭代协议并返回异步生成器(这是另一种迭代器)。
这使我们能够使用for...await...of
循环,它几乎就像for...of
循环,并且是JavaScript中事物循环的一种流行方式,从而使读者的眼睛非常清楚这里。 p>
不幸的是,当创建新的HTTP服务器时,服务器对象本身不会实现异步迭代器协议,因此,如果我们真的需要更深入地推动此问题,为什么我们需要自己实现它。
import http from "http"
const getBody = async request => {
let body = ""
for await (const chunk of request) {
body += chunk.toString()
}
return body
}
const createServer = (port, host) => {
const server = http.createServer()
server[Symbol.asyncIterator] = function() {
return {
next() {
return new Promise((resolve, reject) => {
const onRequest = (request, response) => {
resolve({
done: false,
value: { request, response }
})
server.off("request", onRequest)
}
server.on("request", onRequest)
})
}
}
}
return server
}
const server = createServer()
server.listen(8000, "0.0.0.0", () => {
console.log("Server listening")
})
for await (const {request, response} of server) {
if (request.method === "POST") {
const body = await getBody(request)
console.log(body)
}
response.end("OK")
}
我认为,我认为在Node.js中实现它可能很棒,并且可以使Web服务器的写作非常容易,因为想要使用内置的HTTP服务器的人们。
当然,这不是替代其他路由器,例如Fastify的Express,它将更多的东西带到餐桌上,但这将是从Deno等其他语言中获得灵感的好方法,例如DeNo蓬勃发展,可以使用户的生活更加轻松。