[](()进程与线程
每当我们运行应用程序时,操作系统都会创建该应用程序的实例对象,该实例对象就是应用程序的进程,操作系统会按照进程为单位为应用程序分配资源,比如内存,这样程序才能够在计算机的操作系统中运行起来。
线程被包裹在进程之中,是进程中的实际运作单位,一条线程指的就是进程中的一个单一顺序的控制流。也就是说,应用程序要做的事情都存储在线程之中。可以这样认为,一条线程就是一个待办列表,供 CPU 执行。
[](()JS 单线程 OR 多线程 ?
在 Node.js 代码运行环境中,它为 JavaScript 代码的执行提供了一个主线程,通常我们所说的单线程指的就是这个主线程,主线程用来执行所有的同步代码。但是 Node.js 代码运行环境本身是由 C++ 开发的,在 Node.js 内部它依赖了一个叫做 libuv 的 c++ 库,在这个库中它维护了一个线程池,默认情况下在这个线程池中存储了 4 个线程,JavaScript 中的异步代码就是在这些线程中执行的,所以说 JavaScript 代码的运行依靠了不止一个线程,所以 JavaScript 本质上还是多线程的。
const crypto = require(‘crypto’)
const NUM_REQUESTS = 2;
for (let i = 0; i < NUM_REQUESTS; i++) {
crypto.pbkdf2Sync(‘srcret’, ‘salt’, 10000, 512, ‘sha512’)
}
const crypto = require(‘crypto’)
const NUM_REQUESTS = 2;
for (let i = 0; i < NUM_REQUESTS; i++) {
crypto.pbkdf2(‘srcret’, ‘salt’, 10000, 512, ‘sha512’)
}
[](()基于回调函数的异步编程
- 什么是回调函数
回调函数是指通过函数参数的方式将一个函数传递到另一个函数中,参数函数就是回调函数。
function A() {
console.log(“A is running”)
}
function B(callback) {
console.log(“B Start”)
callback() // A is running
console.log(“B End”)
}
B(A)
我们经常将回调函数写成 callback,实际上它是 call then back 的简写,含义是调用后返回,就是在主函数中调用参数函数,参数函数调用完成后返回主函数继续执行主函数中的代码。
为什么在 B 函数中不直接调用 A 函数而要通过参数的方式传递进去 ?
通常在编写应用程序时,B 函数都是语言内部或者其他开发者定义好的,我们看不到内部代码或者说不能直接在他内部代码中插入我们的代码,而我们又想介入程序的执行,此时就可以通过回调函数的方式将我们的逻辑传递给 B 函数,B 函数在内部再来调用这个回调函数。
- 回调函数传递参数
在主函数中调用回调函数时,可以为回调函数传递参数。
function A(arg) {
console.log(“A is running”)
console.log(arg)
}
function B(callback) {
console.log(“B Start”)
callback(“我是B函数传递给A函数的参数”) // A is running
console.log(“B End”)
}
B(A)
- 回调函数在异步编程中的应用
在异步编程中,异步 API 执行的结果就是通过回调函数传递参数的方式传递到上层代码中的。
const fs = require(“fs”)
fs.readFile(“./index.html”, “utf-8”, function (error, data) {
if (error) console.log(“发生了错误”)
console.log(data)
})
- 回调地狱
回调地狱是回调函数多层嵌套导致代码难以维护的问题。
基于回调函数的异步编程一不小心就会产生回调地狱的问题。
const fs = require(“fs”)
fs.readFile(“./x.txt”, “utf-8”, function (error, x) {
fs.readFile(“./y.txt”, “utf-8”, function (error, y) {
fs.readFile(“./z.txt”, “utf-8”, function (error, z) {
console.log(x)
console.log(y)
console.log(z)
})
})
})
const x = fs.readFile(‘./x.txt’, ‘utf-8’)
const y = fs.readFile(‘./y.txt’, ‘utf-8’)
const z = fs.readFile(‘./z.txt’, ‘utf-8’)
console.log(x)
console.log(y)
console.log(z)
[](()基于 Promise 的异步编程
- Promise 概述
Promise 是 JavaScript 中异步编程解决方案,可以解决回调函数方案中的回调地狱问题。
可以将 Promise 理解为容器,用于包裹异步 API 的容器,当容器中的异步 API 执行完成后,Promise 允许我们在容器的外面获取异步 API 的执行结果,从而避免回调函数嵌套。
Promise 翻译为承若,表示它承若帮我们做一些事情,既然它承若了它就要去做,做就会有一个过程,就会有一个结果,结果要么是成功要么是失败。
所以在 Promise 中有三种状态, 分别为等待(pending),成功(fulfilled),失败(rejected)。
默认状态为等待,等待可以变为成功,等待可以变为失败。
状态一旦更改不可改变,成功不能变回等待,失败不能变回等待,成功不能变成失败,失败不能变成成功。
- Promise 基础语法
const fs = require(“fs”)
const promise = new Promise(function (resolve, reject) {
fs.readFile(“./x.txt”, “utf-8”, function (error, data) {
if (error) {
// 将状态从等待变为失败
reject(error)
} else {
// 将状态从等待变为成功
resolve(data)
}
})
})
promise
.then(function (data) {
console.log(data)
})
.catch(function (error) {
console.log(error)
})
- Promise 链式调用
const fs = require(“fs”)
function readFile(path) {
return new Promise(function (resolve, reject) {
fs.readFile(path, “utf-8”, function (error, data) {
if (error) return reject(error)
resolve(data)
})
})
}
readFile(“./x.txt”)
.then(function (x) {
console.log(x)
return readFile(“./y.txt”)
})
.then(function (y) {
console.log(y)
return readFile(“./z.txt”)
})
.then(function (z) {
console.log(z)
})
.catch(function (error) {
console.log(error)
})
.finally(function () {
console.log(“finally”)
})
- Promise.all 并发异步操作
const fs = require(“fs”)
Promise.all([
readFile(“./x.txt”),
readFile(“./y.txt”),
readFile(“./z.txt”)
]).then(function (data) {
console.log(data)
})
[](()基于异步函数的异步编程
Promise 虽然解决了回调地狱的问题,但是代码看起来仍然不简洁。
使用异步函数简化代码提高异步编程体验。
- 异步函数概述
const fs = require(“fs”)
《大厂前端面试题解析+Web核心总结学习笔记+企业项目实战源码+最新高清讲解视频》无偿开源 徽信搜索公众号【编程进阶路】
function readFile(path) {
return new Promise(function (resolve, reject) {
fs.readFile(path, “utf-8”, function (error, data) {
if (error) return reject(error)
resolve(data)
})
})
}
async function getFileContent() {
let x = await readFile(“./x.txt”)
let y = await readFile(“./y.txt”)
let z = await readFile(“./z.txt”)
return [x, y, z]
}
getFileContent().then(console.log)
async 声明异步函数的关键字,异步函数的返回值会被自动填充到 Promise 对象中。
await 关键字后面只能放置返回 Promise 对象的 API。
await 关键字可以暂停函数执行,等待 Promise 执行完后返回执行结果。
await 关键字只能出现在异步函数中。
- util.promisify
在 Node.js 平台下,所有异步方法使用的都是基于回调函数的异步编程。为了使用异步函数提高异步编程体验,可以使用 util 模块下面的 promisify 方法将基于回调函数的异步 API 转换成返回 Promise 的API。
const fs = require(“fs”)
const util = require(“util”)
const readFile = util.promisify(fs.readFile)
async function getFileContent() {
let x = await readFile(“./x.txt”, “utf-8”)
let y = await readFile(“./y.txt”, “utf-8”)
let z = await readFile(“./z.txt”, “utf-8”)
return [x, y, z]
}
getFileContent().then(console.log)
[]((). Event Loop 机制概述
- 为什么要学习事件循环机制?
学习事件循环可以让开发者明白 JavaScript 的运行机制是怎么样的。
- 事件循环机制做的是什么事情?
事件循环机制用于管理异步 API 的回调函数什么时候回到主线程中执行。
Node.js 采用的是异步 I/O 模型。同步 API 在主线程中执行,异步 API 在底层的 C++ 维护的线程中执行,异步 API 的回调函数在主线程中执行。在 JavaScript 应用运行时,众多异步 API 的回调函数什么时候能回到主线程中调用呢?这就是事件循环机制做的事情,管理异步 API 的回调函数什么时候回到主线程中执行。
- 为什么这种机制叫做事件循环?
因为 Node.js 是事件驱动的。事件驱动就是当什么时候做什么事情,做的事情就定义在回调函数中,可以将异步 API 的回调函数理解为事件处理函数,所以管理异步API回调函数什么时候回到主线程中调用的机制叫做事件循环机制。
[](()Event Loop 的六个阶段
事件循环是一个循环体,在循环体中有六个阶段,在每个阶段中,都有一个事件队列,不同的事件队列存储了不同类型的异步API 的回调函数。
-
Timers:用于存储定时器的回调函数(setInterval, setTimeout)。
-
Pending callbacks:执行与操作系统相关的回调函数,比如启动服务器端应用时监听端口操作的回调函数就在这里调用。
-
Idle, prepare:系统内部使用。
-
IO Poll:存储 I/O 操作的回调函数队列,比如文件读写操作的回调函数。
如果事件队列中有回调函数,执行它们直到清空队列。
否则事件循环将在此阶段停留一段时间以等待新的回调函数进入,这个等待取决于以下两个条件:
-
setImmediate 队列(check 阶段)中存在要执行的回调函数.
-
timers 队列中存在要执行的回调函数. 在这种情况下, 事件循环将移至 check 阶段, 然后移至 Closing callbacks 阶段, 并最终从 timers 阶段进入下一次循环。
-
Check:存储 setImmediate API 的回调函数。
-
Closing callbacks:执行与关闭事件相关的回调,例如关闭数据库连接的回调函数等。
循环体会不断运行以检测是否存在没有调用的回调函数,事件循环机制会按照先进先出的方式执行他们直到队列为空。
[](()宏任务与微任务
[](()宏任务与微任务
宏任务:setInterval, setTimeout, setImmediate, I/O
微任务:Promise.then Promise.catch Promise.finally, process.nextTick