nodejs教程笔记(四)fs续 同步异步

感谢b站教程《Nodejs教程_Nodejs+Koa2入门实战视频教程-2020年更新》
参考:

  1. 同步异步:https://www.liaoxuefeng.com/wiki/1022910821149312/1023025763380448
  2. 回调函数callback:
    https://blog.csdn.net/rockage/article/details/79513450
  3. promise:https://www.liaoxuefeng.com/wiki/1022910821149312/1023024413276544

一、同步、异步

fs模块同时提供了异步和同步的方法。回顾一下什么是异步方法。因为JavaScript的单线程模型,执行IO操作时,JavaScript代码无需等待,而是传入回调函数后,继续执行后续JavaScript代码。比如jQuery提供的getJSON()操作:

$.getJSON('http://example.com/ajax', function (data) {
    console.log('IO结果返回后执行...');
});
console.log('不等待IO结果直接执行后续代码...');

同步的IO操作则需要等待函数返回

// 根据网络耗时,函数将执行几十毫秒~几秒不等:
var data = getJSONSync('http://example.com/ajax');

同步操作的好处是代码简单,缺点是程序将等待IO操作,在等待时间内,无法响应其它任何事件。而异步读取不用等待IO操作,但代码较麻烦。
同步方法不接收回调函数,如果同步读取文件发生错误,则需要用try…catch捕获该错误。

try {
    var data = fs.readFileSync('sample.txt', 'utf-8');
    console.log(data);
} catch (err) {
    // 出错了
}

异步读文件时,可以直接用回调函数捕捉错误。

fs.readFile('sample.png', function (err, data) {
    if (err) {
        console.log(err);
    } else {
        console.log(data);
        console.log(data.length + ' bytes');
    }
});

由于Node环境执行的JavaScript代码是服务器端代码,所以,绝大部分需要在服务器运行期反复执行业务逻辑的代码,必须使用异步代码,否则,同步代码在执行时期,服务器将停止响应,因为JavaScript只有一个执行线程。
服务器启动时如果需要读取配置文件,或者结束时需要写入到状态文件时,可以使用同步代码,因为这些代码只在启动和结束时执行一次,不影响服务器正常运行时的异步执行。

二、递归解决问题2

问题描述:wwwroot文件夹下面有images css js 以及index.html,找出wwwroot目录下所有目录,然后放在一个数组中

var path = './wwwroot'
var dirArr = [];

fs.readdir(path, (err, data)=>{
    if(err) {
        console.log(err);
        return;
    }
    // 递归实现
    (function getDir(i){
        if(i == data.length) { // 执行完成
            console.log(dirArr)
            return
        }

        fs.stat(path+'/'+data[i], (error, stats)=>{
            if(stats.isDirectory()) {
                dirArr.push(data[i])
            }
            getDir(i+1)
        })
    })(0)
})

三、新特性

  1. let和var一样的用来定义变量,而let、const是块作用域
  2. 属性的简写
var name = 'zhangsan'
var app = {
    "name":name
}
console.log(app.name)
// 如果属性名称和变量名一样,则可如下简写
var name = 'zhangsan'
var app = {
    name
}
console.log(app.name)
  1. 方法的简写
var name = 'zhangsan'
var app = {
    name,
    run:function() {
        console.log(`${this.name}在跑步`)
    }
}
app.run()
// 简写后
var name = 'zhangsan'
var app = {
    name,
    run() {
        console.log(`${this.name}在跑步`)
    }
}
app.run()
  1. 箭头函数
setTimeout(function(){
    console.log('执行')
}, 1000)
// 改写后
setTimeout(()=>{
    console.log('执行')
}, 1000)

四、处理异步

4.1 回调函数callback

在大多数编程语言中,函数的形参总是由外往内向函数体传递参数,但在JS里如果形参是关键字"callback"则完全相反,它表示函数体在完成某种操作后由内向外调用某个外部函数。

// callback传异步数据
function getData(callback) {
    // ajax
    setTimeout(()=>{
        var name = 'zhangsan'
        callback(name);
    }, 1000);
}

// 外部获取异步方法里的数据
getData((aaa)=>{
    console.log(aaa)
})

getData的参数为callback函数,callback函数在后期调用时被赋值为了(aaa)=>{console.log(aaa)},将函数传入getData函数中替代callback。因为callback是在getData函数里的,所以输出的name为赋值好的’zhangsan’。

4.2 ES6中新定义的promise解决异步传参数
  1. promise介绍

我们先看一个最简单的Promise例子:生成一个0-2之间的随机数,如果小于1,则等待一段时间后返回成功,否则返回失败:

function test(resolve, reject) {
    var timeOut = Math.random() * 2;
    log('set timeout to: ' + timeOut + ' seconds.');
    setTimeout(function () {
        if (timeOut < 1) {
            log('call resolve()...');
            resolve('200 OK');
        }
        else {
            log('call reject()...');
            reject('timeout in ' + timeOut + ' seconds.');
        }
    }, timeOut * 1000);
}

这个test()函数有两个参数,这两个参数都是函数,如果执行成功,我们将调用resolve(‘200 OK’),如果执行失败,我们将调用reject(‘timeout in ’ + timeOut + ’ seconds.’)。可以看出,test()函数只关心自身的逻辑,并不关心具体的resolve和reject将如何处理结果。
有了执行函数,我们就可以用一个Promise对象来执行它,并在将来某个时刻获得成功或失败的结果:

扫描二维码关注公众号,回复: 9235774 查看本文章
var p1 = new Promise(test);
var p2 = p1.then(function (result) {
    console.log('成功:' + result);
});
var p3 = p2.catch(function (reason) { // 失败时用catch
    console.log('失败:' + reason);
});

变量p1是一个Promise对象,它负责执行test函数。由于test函数在内部是异步执行的,当test函数执行成功时,我们告诉Promise对象:

// 如果成功,执行这个函数:
p1.then(function (result) {
    console.log('成功:' + result);
});

当test函数执行失败时,我们告诉Promise对象:

p2.catch(function (reason) {
    console.log('失败:' + reason);
});
  1. 串行执行异步任务

Promise还可以做更多的事情,比如,有若干个异步任务,需要先做任务1,如果成功后再做任务2,任何任务失败则不再继续并执行错误处理函数。
要串行执行这样的异步任务,不用Promise需要写一层一层的嵌套代码。有了Promise,我们只需要简单地写:

job1.then(job2).then(job3).catch(handleError); // 可以统一用一个catch处理错误

其中,job1、job2和job3都是Promise对象。

  1. 并行执行异步任务Promise.all()

除了串行执行若干异步任务外,Promise还可以并行执行异步任务。
试想一个页面聊天系统,我们需要从两个不同的URL分别获得用户的个人信息和好友列表,这两个任务是可以并行执行的,用Promise.all()实现如下:

var p1 = new Promise(function (resolve, reject) {
    setTimeout(resolve, 500, 'P1');
});
var p2 = new Promise(function (resolve, reject) {
    setTimeout(resolve, 600, 'P2');
});
// 同时执行p1和p2,并在它们都完成后执行then:
Promise.all([p1, p2]).then(function (results) {
    console.log(results); // 获得一个Array: ['P1', 'P2']
});
  1. 异步任务容错 Promise.race()

有些时候,多个异步任务是为了容错。比如,同时向两个URL读取用户的个人信息,只需要获得先返回的结果即可。这种情况下,用Promise.race()实现:

var p1 = new Promise(function (resolve, reject) {
    setTimeout(resolve, 500, 'P1');
});
var p2 = new Promise(function (resolve, reject) {
    setTimeout(resolve, 600, 'P2');
});
Promise.race([p1, p2]).then(function (result) {
    console.log(result); // 'P1'
});

由于p1执行较快,Promise的then()将获得结果’P1’。p2仍在继续执行,但执行结果将被丢弃。

4.3 Async、Await
  1. async用于声明一个异步的function,await用于等待一个异步方法执行完成
  2. async返回字符串并用await获取(不建议)
async function test(){ // 会自动将返回值包装为promise
    return "你好nodejs"
}

console.log(test())

输出:Promise { ‘你好nodejs’ }
若要获得“你好nodejs”这个字符串,而不是promise,则需要用await函数。需要注意await要定义在异步方法中。

async function main() {
    var data = await test(); // 获取异步方法里数据
    console.log(data);
}
main();
  1. async返回promise对象并用await获取
async function test() { // 会自动将返回值包装为promise
    return new Promise((resolve, reject) => {
        setTimeout(()=>{
            var name = 'zhangsan'
            resolve(name);
        }, 1000);
    })
}

async function main() {
    var data = await test(); // 获取异步方法里数据
    console.log(data);
}
main();
  1. 练习
    wwwroot文件夹下面有images css js 以及index.html,找出wwwroot目录下所有目录,然后放在一个数组中
fs = require('fs')

const path = './wwwroot'
let dirArr = [];

// 1. 定义一个isDir方法判断资源是目录还是文件
async function isDir(path)
{
    return new Promise((resolve, reject) => {
        fs.stat(path, (error, stats)=>{
            if(error) {
                console.log(error)
                reject(error)
                return;
            }
            if(stats.isDirectory()) {
                resolve(true);
            } else {
                resolve(false);
            }
        })
    })
}

// 2. 获取wwwroot
function main() {

  // 哪个方法里需要await,就把哪个直接外部方法设置为async
  fs.readdir(path, async (err, data) => {
    if (err) {
      console.log(err)
      return
    }

    for (let i = 0; i < data.length; i++) {
      // await语句执行完之后才会执行下面的语句
      if (await isDir(path+'/'+data[i])) {
        dirArr.push(data[i])
      }
    }

    console.log(dirArr)
  })
}

main()

发布了220 篇原创文章 · 获赞 28 · 访问量 8万+

猜你喜欢

转载自blog.csdn.net/Ema1997/article/details/104355643