本文参考原文-http://bjbsair.com/2020-03-22/tech-info/2825.html
在我的node.js系列的开篇,有介绍过node.js具有单线程,异步,非阻塞的特点。如何理解这些特点?举一个例子,如果遇到耗时操作,比如网络交互或者磁盘IO,是不是需要等待操作结束再执行下一步操作?
对于node.js来讲,属于单线程,如果需要等待操作结束再执行后面的操作就麻烦了。
node.js选择的方式是在发起一个调用后不等待结果,继续向下执行。node.js这里采用的机制是异步+回调,通过异步和回调来实现对高并发的支持。
回调函数
将一个函数作为参数传递给另一个函数,并且作为参数的函数可以被执行,其本质上是一个高阶函数。
我们用之前介绍过的文件模块中的函数举例。
例如,在执行读文件操作时,可以使用readFile方法。这个方法就是用了回调函数。
fs.readFile('./test.txt', (err, data)=>{
if(err){
consolog.log(err)
return
}
console.log(data.toString())
})
嵌套回调
如果我们写代码需要读取三个文件,按照以前的方式,我们无法知道哪个文件的读取先结束。也就是说,如果我们需要先读取A文件,再读取B文件,只能进行回调函数的嵌套调用。
//先读取A
fs.readFile('./A.txt', (err, data)=>{
if(err){
consolog.log(err)
return
}
//再读取B
fs.readFile('./A.txt', (err, data)=>{
}
})
如果需要读取多个文件并有明确的顺序要求,这个代码的可读性就很糟糕了。
在文件模块里,node.js还提供了另一个方法fs.readFileSync()。这是一个同步函数,可以直接得到结果。
但在业务逻辑中,面对大量的回调函数,如何进行操作呢?
使用Promise
Promise是对异步操作的封装,提供了三个状态。
- 操作在执行中: Pending
- 操作成功: Resolved
- 操作失败: Rejected
从上面可以看出,pending是一个中间状态,一旦一个异步操作执行完成以后,或者转换为Resolved,或者转换为Rejected。
要使用Promise,首先需要用Promise的构造函数来封装一个现有的异步操作。我们以fs.readFile为例。
function readFilePromise(path){
//初始化Promise
return new Promise(function(resolve, reject){
fs.readFile(path, (err, data)=>{
if(err){
reject(err)
}else{
resolve(data)
}
}
})
}
在回调函数中,需要将不同的返回结果传入resolve或者reject中。上例中,我们将error传入reject,表示读取文件出错,将data传入resolve,表示操作成功。
对于上面封装后的Promise,调用的时候可以通过then()来获取异步操作的值,即resolve的值。通过catch()方法来reject中的错误。
promise
.then(function(data){
//成功
}).catch(function(data){
//出错
});
回到之前的问题,如果需要按顺序读取三个文件A,B,C。那么此时调用前面的promise,就可以promise链式调用。
readFilePromise("./A.txt").then(function(data){
console.log(data);
return readFilePromise("./B.txt");
}).then(function(data){
console.log(data);
return readFilePromise("./C.txt");
}).then(function(data){
console.log(data);
})
看起来还是有点绕,有没有办法还是让代码实际异步执行,但程序看上去和同步一样呢?
async/await
这就是现在使用起来最为方便的async/await。程序在使用了async/await后看上去可以和同步的代码一样,可读性很强。
还是以刚才顺序读取三个文件为例。
const fs = require('fs');
async function readData(fpath){
//顺序读取三个文件
let fa = await fs.readFile('./A.txt');
let fb = await fs.readFile('./A.txt');
let fc = await fs.readFile('./A.txt');
}
readData(fpath);
上面简洁的代码可以顺序读取三个文件A,B,C。最后只需要直接调用即可。
需要注意的是,在使用async/await时,函数前面一定要写async,在函数体中,对于异步函数的调用,一定要写await。在函数外调用该函数时,直接写函数名即可。
总结
从回调函数,到Promise,再到async/await,显示了node.js异步操作的不断演化。在async/await之前还有一个过度方案generator。目前实际使用中,async/await居多。
后面会介绍node.js体系里的另一个web框架Koa,和express不一样的地方其中就有异步的处理。Koa就是使用了async/await。
如果有什么问题,欢迎大家留言讨论。本文参考原文-http://bjbsair.com/2020-03-22/tech-info/2825/
在我的node.js系列的开篇,有介绍过node.js具有单线程,异步,非阻塞的特点。如何理解这些特点?举一个例子,如果遇到耗时操作,比如网络交互或者磁盘IO,是不是需要等待操作结束再执行下一步操作?
对于node.js来讲,属于单线程,如果需要等待操作结束再执行后面的操作就麻烦了。
node.js选择的方式是在发起一个调用后不等待结果,继续向下执行。node.js这里采用的机制是异步+回调,通过异步和回调来实现对高并发的支持。
回调函数
将一个函数作为参数传递给另一个函数,并且作为参数的函数可以被执行,其本质上是一个高阶函数。
我们用之前介绍过的文件模块中的函数举例。
例如,在执行读文件操作时,可以使用readFile方法。这个方法就是用了回调函数。
fs.readFile('./test.txt', (err, data)=>{
if(err){
consolog.log(err)
return
}
console.log(data.toString())
})
嵌套回调
如果我们写代码需要读取三个文件,按照以前的方式,我们无法知道哪个文件的读取先结束。也就是说,如果我们需要先读取A文件,再读取B文件,只能进行回调函数的嵌套调用。
//先读取A
fs.readFile('./A.txt', (err, data)=>{
if(err){
consolog.log(err)
return
}
//再读取B
fs.readFile('./A.txt', (err, data)=>{
}
})
如果需要读取多个文件并有明确的顺序要求,这个代码的可读性就很糟糕了。
在文件模块里,node.js还提供了另一个方法fs.readFileSync()。这是一个同步函数,可以直接得到结果。
但在业务逻辑中,面对大量的回调函数,如何进行操作呢?
使用Promise
Promise是对异步操作的封装,提供了三个状态。
- 操作在执行中: Pending
- 操作成功: Resolved
- 操作失败: Rejected
从上面可以看出,pending是一个中间状态,一旦一个异步操作执行完成以后,或者转换为Resolved,或者转换为Rejected。
要使用Promise,首先需要用Promise的构造函数来封装一个现有的异步操作。我们以fs.readFile为例。
function readFilePromise(path){
//初始化Promise
return new Promise(function(resolve, reject){
fs.readFile(path, (err, data)=>{
if(err){
reject(err)
}else{
resolve(data)
}
}
})
}
在回调函数中,需要将不同的返回结果传入resolve或者reject中。上例中,我们将error传入reject,表示读取文件出错,将data传入resolve,表示操作成功。
对于上面封装后的Promise,调用的时候可以通过then()来获取异步操作的值,即resolve的值。通过catch()方法来reject中的错误。
promise
.then(function(data){
//成功
}).catch(function(data){
//出错
});
回到之前的问题,如果需要按顺序读取三个文件A,B,C。那么此时调用前面的promise,就可以promise链式调用。
readFilePromise("./A.txt").then(function(data){
console.log(data);
return readFilePromise("./B.txt");
}).then(function(data){
console.log(data);
return readFilePromise("./C.txt");
}).then(function(data){
console.log(data);
})
看起来还是有点绕,有没有办法还是让代码实际异步执行,但程序看上去和同步一样呢?
async/await
这就是现在使用起来最为方便的async/await。程序在使用了async/await后看上去可以和同步的代码一样,可读性很强。
还是以刚才顺序读取三个文件为例。
const fs = require('fs');
async function readData(fpath){
//顺序读取三个文件
let fa = await fs.readFile('./A.txt');
let fb = await fs.readFile('./A.txt');
let fc = await fs.readFile('./A.txt');
}
readData(fpath);
上面简洁的代码可以顺序读取三个文件A,B,C。最后只需要直接调用即可。
需要注意的是,在使用async/await时,函数前面一定要写async,在函数体中,对于异步函数的调用,一定要写await。在函数外调用该函数时,直接写函数名即可。
总结
从回调函数,到Promise,再到async/await,显示了node.js异步操作的不断演化。在async/await之前还有一个过度方案generator。目前实际使用中,async/await居多。
后面会介绍node.js体系里的另一个web框架Koa,和express不一样的地方其中就有异步的处理。Koa就是使用了async/await。
如果有什么问题,欢迎大家留言讨论。本文参考原文-http://bjbsair.com/2020-03-22/tech-info/2825/
在我的node.js系列的开篇,有介绍过node.js具有单线程,异步,非阻塞的特点。如何理解这些特点?举一个例子,如果遇到耗时操作,比如网络交互或者磁盘IO,是不是需要等待操作结束再执行下一步操作?
对于node.js来讲,属于单线程,如果需要等待操作结束再执行后面的操作就麻烦了。
node.js选择的方式是在发起一个调用后不等待结果,继续向下执行。node.js这里采用的机制是异步+回调,通过异步和回调来实现对高并发的支持。
回调函数
将一个函数作为参数传递给另一个函数,并且作为参数的函数可以被执行,其本质上是一个高阶函数。
我们用之前介绍过的文件模块中的函数举例。
例如,在执行读文件操作时,可以使用readFile方法。这个方法就是用了回调函数。
fs.readFile('./test.txt', (err, data)=>{
if(err){
consolog.log(err)
return
}
console.log(data.toString())
})
嵌套回调
如果我们写代码需要读取三个文件,按照以前的方式,我们无法知道哪个文件的读取先结束。也就是说,如果我们需要先读取A文件,再读取B文件,只能进行回调函数的嵌套调用。
//先读取A
fs.readFile('./A.txt', (err, data)=>{
if(err){
consolog.log(err)
return
}
//再读取B
fs.readFile('./A.txt', (err, data)=>{
}
})
如果需要读取多个文件并有明确的顺序要求,这个代码的可读性就很糟糕了。
在文件模块里,node.js还提供了另一个方法fs.readFileSync()。这是一个同步函数,可以直接得到结果。
但在业务逻辑中,面对大量的回调函数,如何进行操作呢?
使用Promise
Promise是对异步操作的封装,提供了三个状态。
- 操作在执行中: Pending
- 操作成功: Resolved
- 操作失败: Rejected
从上面可以看出,pending是一个中间状态,一旦一个异步操作执行完成以后,或者转换为Resolved,或者转换为Rejected。
要使用Promise,首先需要用Promise的构造函数来封装一个现有的异步操作。我们以fs.readFile为例。
function readFilePromise(path){
//初始化Promise
return new Promise(function(resolve, reject){
fs.readFile(path, (err, data)=>{
if(err){
reject(err)
}else{
resolve(data)
}
}
})
}
在回调函数中,需要将不同的返回结果传入resolve或者reject中。上例中,我们将error传入reject,表示读取文件出错,将data传入resolve,表示操作成功。
对于上面封装后的Promise,调用的时候可以通过then()来获取异步操作的值,即resolve的值。通过catch()方法来reject中的错误。
promise
.then(function(data){
//成功
}).catch(function(data){
//出错
});
回到之前的问题,如果需要按顺序读取三个文件A,B,C。那么此时调用前面的promise,就可以promise链式调用。
readFilePromise("./A.txt").then(function(data){
console.log(data);
return readFilePromise("./B.txt");
}).then(function(data){
console.log(data);
return readFilePromise("./C.txt");
}).then(function(data){
console.log(data);
})
看起来还是有点绕,有没有办法还是让代码实际异步执行,但程序看上去和同步一样呢?
async/await
这就是现在使用起来最为方便的async/await。程序在使用了async/await后看上去可以和同步的代码一样,可读性很强。
还是以刚才顺序读取三个文件为例。
const fs = require('fs');
async function readData(fpath){
//顺序读取三个文件
let fa = await fs.readFile('./A.txt');
let fb = await fs.readFile('./A.txt');
let fc = await fs.readFile('./A.txt');
}
readData(fpath);
上面简洁的代码可以顺序读取三个文件A,B,C。最后只需要直接调用即可。
需要注意的是,在使用async/await时,函数前面一定要写async,在函数体中,对于异步函数的调用,一定要写await。在函数外调用该函数时,直接写函数名即可。
总结
从回调函数,到Promise,再到async/await,显示了node.js异步操作的不断演化。在async/await之前还有一个过度方案generator。目前实际使用中,async/await居多。
后面会介绍node.js体系里的另一个web框架Koa,和express不一样的地方其中就有异步的处理。Koa就是使用了async/await。
如果有什么问题,欢迎大家留言讨论。本文参考原文-http://bjbsair.com/2020-03-22/tech-info/2825/
在我的node.js系列的开篇,有介绍过node.js具有单线程,异步,非阻塞的特点。如何理解这些特点?举一个例子,如果遇到耗时操作,比如网络交互或者磁盘IO,是不是需要等待操作结束再执行下一步操作?
对于node.js来讲,属于单线程,如果需要等待操作结束再执行后面的操作就麻烦了。
node.js选择的方式是在发起一个调用后不等待结果,继续向下执行。node.js这里采用的机制是异步+回调,通过异步和回调来实现对高并发的支持。
回调函数
将一个函数作为参数传递给另一个函数,并且作为参数的函数可以被执行,其本质上是一个高阶函数。
我们用之前介绍过的文件模块中的函数举例。
例如,在执行读文件操作时,可以使用readFile方法。这个方法就是用了回调函数。
fs.readFile('./test.txt', (err, data)=>{
if(err){
consolog.log(err)
return
}
console.log(data.toString())
})
嵌套回调
如果我们写代码需要读取三个文件,按照以前的方式,我们无法知道哪个文件的读取先结束。也就是说,如果我们需要先读取A文件,再读取B文件,只能进行回调函数的嵌套调用。
//先读取A
fs.readFile('./A.txt', (err, data)=>{
if(err){
consolog.log(err)
return
}
//再读取B
fs.readFile('./A.txt', (err, data)=>{
}
})
如果需要读取多个文件并有明确的顺序要求,这个代码的可读性就很糟糕了。
在文件模块里,node.js还提供了另一个方法fs.readFileSync()。这是一个同步函数,可以直接得到结果。
但在业务逻辑中,面对大量的回调函数,如何进行操作呢?
使用Promise
Promise是对异步操作的封装,提供了三个状态。
- 操作在执行中: Pending
- 操作成功: Resolved
- 操作失败: Rejected
从上面可以看出,pending是一个中间状态,一旦一个异步操作执行完成以后,或者转换为Resolved,或者转换为Rejected。
要使用Promise,首先需要用Promise的构造函数来封装一个现有的异步操作。我们以fs.readFile为例。
function readFilePromise(path){
//初始化Promise
return new Promise(function(resolve, reject){
fs.readFile(path, (err, data)=>{
if(err){
reject(err)
}else{
resolve(data)
}
}
})
}
在回调函数中,需要将不同的返回结果传入resolve或者reject中。上例中,我们将error传入reject,表示读取文件出错,将data传入resolve,表示操作成功。
对于上面封装后的Promise,调用的时候可以通过then()来获取异步操作的值,即resolve的值。通过catch()方法来reject中的错误。
promise
.then(function(data){
//成功
}).catch(function(data){
//出错
});
回到之前的问题,如果需要按顺序读取三个文件A,B,C。那么此时调用前面的promise,就可以promise链式调用。
readFilePromise("./A.txt").then(function(data){
console.log(data);
return readFilePromise("./B.txt");
}).then(function(data){
console.log(data);
return readFilePromise("./C.txt");
}).then(function(data){
console.log(data);
})
看起来还是有点绕,有没有办法还是让代码实际异步执行,但程序看上去和同步一样呢?
async/await
这就是现在使用起来最为方便的async/await。程序在使用了async/await后看上去可以和同步的代码一样,可读性很强。
还是以刚才顺序读取三个文件为例。
const fs = require('fs');
async function readData(fpath){
//顺序读取三个文件
let fa = await fs.readFile('./A.txt');
let fb = await fs.readFile('./A.txt');
let fc = await fs.readFile('./A.txt');
}
readData(fpath);
上面简洁的代码可以顺序读取三个文件A,B,C。最后只需要直接调用即可。
需要注意的是,在使用async/await时,函数前面一定要写async,在函数体中,对于异步函数的调用,一定要写await。在函数外调用该函数时,直接写函数名即可。
总结
从回调函数,到Promise,再到async/await,显示了node.js异步操作的不断演化。在async/await之前还有一个过度方案generator。目前实际使用中,async/await居多。
后面会介绍node.js体系里的另一个web框架Koa,和express不一样的地方其中就有异步的处理。Koa就是使用了async/await。
如果有什么问题,欢迎大家留言讨论。本文参考原文-http://bjbsair.com/2020-03-22/tech-info/2825/
在我的node.js系列的开篇,有介绍过node.js具有单线程,异步,非阻塞的特点。如何理解这些特点?举一个例子,如果遇到耗时操作,比如网络交互或者磁盘IO,是不是需要等待操作结束再执行下一步操作?
对于node.js来讲,属于单线程,如果需要等待操作结束再执行后面的操作就麻烦了。
node.js选择的方式是在发起一个调用后不等待结果,继续向下执行。node.js这里采用的机制是异步+回调,通过异步和回调来实现对高并发的支持。
回调函数
将一个函数作为参数传递给另一个函数,并且作为参数的函数可以被执行,其本质上是一个高阶函数。
我们用之前介绍过的文件模块中的函数举例。
例如,在执行读文件操作时,可以使用readFile方法。这个方法就是用了回调函数。
fs.readFile('./test.txt', (err, data)=>{
if(err){
consolog.log(err)
return
}
console.log(data.toString())
})
嵌套回调
如果我们写代码需要读取三个文件,按照以前的方式,我们无法知道哪个文件的读取先结束。也就是说,如果我们需要先读取A文件,再读取B文件,只能进行回调函数的嵌套调用。
//先读取A
fs.readFile('./A.txt', (err, data)=>{
if(err){
consolog.log(err)
return
}
//再读取B
fs.readFile('./A.txt', (err, data)=>{
}
})
如果需要读取多个文件并有明确的顺序要求,这个代码的可读性就很糟糕了。
在文件模块里,node.js还提供了另一个方法fs.readFileSync()。这是一个同步函数,可以直接得到结果。
但在业务逻辑中,面对大量的回调函数,如何进行操作呢?
使用Promise
Promise是对异步操作的封装,提供了三个状态。
- 操作在执行中: Pending
- 操作成功: Resolved
- 操作失败: Rejected
从上面可以看出,pending是一个中间状态,一旦一个异步操作执行完成以后,或者转换为Resolved,或者转换为Rejected。
要使用Promise,首先需要用Promise的构造函数来封装一个现有的异步操作。我们以fs.readFile为例。
function readFilePromise(path){
//初始化Promise
return new Promise(function(resolve, reject){
fs.readFile(path, (err, data)=>{
if(err){
reject(err)
}else{
resolve(data)
}
}
})
}
在回调函数中,需要将不同的返回结果传入resolve或者reject中。上例中,我们将error传入reject,表示读取文件出错,将data传入resolve,表示操作成功。
对于上面封装后的Promise,调用的时候可以通过then()来获取异步操作的值,即resolve的值。通过catch()方法来reject中的错误。
promise
.then(function(data){
//成功
}).catch(function(data){
//出错
});
回到之前的问题,如果需要按顺序读取三个文件A,B,C。那么此时调用前面的promise,就可以promise链式调用。
readFilePromise("./A.txt").then(function(data){
console.log(data);
return readFilePromise("./B.txt");
}).then(function(data){
console.log(data);
return readFilePromise("./C.txt");
}).then(function(data){
console.log(data);
})
看起来还是有点绕,有没有办法还是让代码实际异步执行,但程序看上去和同步一样呢?
async/await
这就是现在使用起来最为方便的async/await。程序在使用了async/await后看上去可以和同步的代码一样,可读性很强。
还是以刚才顺序读取三个文件为例。
const fs = require('fs');
async function readData(fpath){
//顺序读取三个文件
let fa = await fs.readFile('./A.txt');
let fb = await fs.readFile('./A.txt');
let fc = await fs.readFile('./A.txt');
}
readData(fpath);
上面简洁的代码可以顺序读取三个文件A,B,C。最后只需要直接调用即可。
需要注意的是,在使用async/await时,函数前面一定要写async,在函数体中,对于异步函数的调用,一定要写await。在函数外调用该函数时,直接写函数名即可。
总结
从回调函数,到Promise,再到async/await,显示了node.js异步操作的不断演化。在async/await之前还有一个过度方案generator。目前实际使用中,async/await居多。
后面会介绍node.js体系里的另一个web框架Koa,和express不一样的地方其中就有异步的处理。Koa就是使用了async/await。
如果有什么问题,欢迎大家留言讨论。