I/O 操作的模型有两种
第一种是 CPU 等待 I/O 操作执行完成获取到操作结果后再去执行其他指令,这是同步 I/O 操作 (阻塞
I/O)。
第二种是 CPU 不等待 I/O 操作执行完成,CPU 在发出 I/O 指令后,内存和磁盘开始工作,CPU 继续执
行其他指令。当 I/O 操作完成后再通知 CPU I/O 操作的结果是什么。这是异步 I/O 操作 (非阻塞 I/O) 。
同步 I/O 在代码中的表现就是代码暂停执行等待 I/O 操作,I/O 操作执行完成后再执行后续代码。
异步 I/O 在代码中的表现就是代码不暂停执行,I/O 操作后面的代码可以继续执行,当 I/O 操作执行完成
后通过回调函数的方式通知 CPU,说 I/O 操作已经完成了,基于 I/O 操作结果的其他操作可以执行了
(通知 CPU 调用回调函数)。
同步 I/O 和 异步 I/O 区别就是是否等待 I/O 结果。
Node 采用的就是异步非阻塞 I/O 模型。
在 Node.js 代码运行环境中,它为 JavaScript 代码的执行提供了一个主线程,通常我们所说的单线程指
的就是这个主线程,主线程用来执行所有的同步代码。但是 Node.js 代码运行环境本身是由 C++ 开发
的,在 Node.js 内部它依赖了一个叫做 libuv 的 c++ 库,在这个库中它维护了一个线程池,默认情况下
在这个线程池中存储了 4 个线程,JavaScript 中的异步代码就是在这些线程中执行的,所以说
JavaScript 代码的运行依靠了不止一个线程,所以 JavaScript 本质上还是多线程的。
函数作为参数传递给另一个函数调用
function Thread1(log)
{
console.log(log);
}
function MainThread(callBack)
{
console.log("MainThread Start:");
callBack("Thread1 Start:");
console.log("MainThread End:");
}
MainThread(Thread1);
const fs = require('fs');
fs.readFile('./test.txt', "utf-8",(err, data) =>
{
if(err) console.log(err + "error is happened.");
else console.log(data);
})
回调函数如果出现嵌套将可能产生问题
可以通过Promise函数 和 异步编程的方法解决。
可以将 Promise 理解为容器,用于包裹异步 API 的容器,当容器中的异步 API 执行完成后,
Promise 允许我们在容器的外面获取异步 API 的执行结果,从而避免回调函数嵌套。
Promise 中有三种状态, 分别为等待(pending),成功(fulfilled),失败(rejected)。
默认状态为等待,等待可以变为成功,等待可以变为失败。 状态一旦更改不可改变。
//promise方法
const promise = new Promise((resolve, reject) => {
fs.readFile("file1.txt", "utf8", (err, data) => { //通过回调函数读取文件内容
if (err) {
reject(err);
} else {
resolve(data);
}
})
})
promise.then((data) => { //执行成功时打印(串行)
console.log(data);
})
.catch(
(err) => {
console.log(err); //执行失败时打印(串行)
}
);
//promise的链式调用
function readFile(path)
{
return new Promise(
(resolve, reject) =>
{
fs.readFile(path, "utf8", (err, data) => {
if (err) {
reject(err);
} else {
resolve(data);
}
})
}
)
}
readFile("file1.txt").then
(
(data) =>{
console.log(data);
return readFile("file2.txt").then(
function(data){
console.log(data).catch(
function(data){
console.log(data).finally(
function(){
console.log("finally")
}
)
}
)
}
)
}
)
Promise.finally([
readFile("file1.txt"),
readFile("file2.txt"),
readFile("file3.txt")
]).then(
(data) =>{
console.log(data);
}
)
使用异步方法同样可以解决回调嵌套的问题
如下关键字:
async 声明异步函数的关键字,异步函数的返回值会被自动填充到 Promise 对象中。
await 关键字后面只能放置返回 Promise 对象的 API。
await 关键字可以暂停函数执行,等待 Promise 执行完后返回执行结果。
await 关键字只能出现在异步函数中。
async function async2()
{
let x = await readFile("file1.txt");
let y = await readFile("file2.txt");
let z = await readFile("file3.txt");
return [x,y,z];
}
async2().then(console.log)
在 Node.js 平台下,所有异步方法使用的都是基于回调函数的异步编程。为了使用异步函数提高异
步编程体验,可以使用 util 模块下面的 promisify 方法将基于回调函数的异步 API 转换成返回
Promise 的API。
const util = require("util");
const rf = util.promisify(fs.readFile);
async function async3()
{
let x = await rf("file1.txt");
let y = await rf("file2.txt");
let z = await rf("file3.txt");
return [x,y,z];
}
async3().then(console.log)