持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第8天,点击查看活动详情
同步API我们是可以直接用返回值获取结果的,但是异步API是不行的,异步API需要用回调函数的形式,用回调函数的形参接收返回结果
console.log(1)
// 同步代码在执行时,这个setTimout也会执行,只不过回调函数当前不会调用,会先推入到异步回调队列中,等达到执行条件了,就会调用
setTimout(() => {
console.log(2)
}, 1000)
console.log(3)
回调地狱:指的是多个依次的异步操作,说白了就是回调函数嵌套,当嵌套层次过多,不利于阅读和维护
Promise基本使用:
const fs = require('fs')
// 这个resolve和reject是我们自己定义名字,你可以改成其他,但是不推荐,业内约定俗成就叫这个
const p = new Promise((resolve, reject) => {
fs.readFile('./hehe.txt', 'utf8', (err, data) => {
if (err) {
reject(err)
} else {
resolve(data)
}
})
})
p.then((data) => {
// 异步API执行正确,拿到正确的数据
// your code . do something...
}).catch((err) => {
// 异步API执行异常,拿到错误的数据
// your code... do something...
})
- Promise就是用来解决回调地狱的,,它不是一个新的技术,只是一种手段,是异步编程的中间产物
- 原理:把一个异步API封装起来,将异步API的结果以回调函数的形式传递出去
- 可以通过new Promise( ( resolve, reject ) => { ... }) 创建Promise实例
- 注意:创建实例时,并不会执行
( resolve, reject ) => { ... }回调函数,也就意味着内部的代码都不会执行,只有在调用then或catch方法的时候才会去调用
- 注意:创建实例时,并不会执行
- 可以通过resolve( result )方法传递正确的结果
- 可以通过reject( err )方法传递错误的结果
- 通过调用Promise实例的then(( result ) => { ... })方法接收正确的结果
- 通过调用Promise实例的catch(( err ) => { ... })方法接收错误的结果
解决回调地狱:
const fs = require('fs')
// 想要依次读取1.txt,2.txt,3.txt的文件内容
// fs.readFile('./1.txt', 'utf8', (err, data) => {
// console.log(data)
// fs.readFile('./2.txt', 'utf8', (err, data) => {
// console.log(data)
// fs.readFile('./3.txt', 'utf8', (err, data) => {
// console.log(data)
// })
// })
// })
// new Promise(cb) 实例时,并不会调用这个 cb 函数
// 只有在调用了Promise实例的then方法或catch方法,才会调用cb函数
const p1 = new Promise((yes, no) => {
fs.readFile('./1.txt', 'utf8', (err, data) => {
yes(data)
})
})
const p2 = new Promise((yes, no) => {
fs.readFile('./2.txt', 'utf8', (err, data) => {
yes(data)
})
})
const p3 = new Promise((yes, no) => {
fs.readFile('./3.txt', 'utf8', (err, data) => {
yes(data)
})
})
// then函数的返回值 就是 其参数(回调函数)的返回值
p1.then(data => {
console.log(data)
return p2
}).then(data => {
console.log(data)
return p3
}).then(data => {
console.log(data)
})
异步函数(ES7添加的):
- async关键字:
- 在普通函数前面加上
async关键字,该普通函数就变成了异步函数 - 异步函数的返回值会被包装成一个
Promise实例- 用
return传递正确的结果,相当于Promise里面的resolve函数 - 用
throw传递错误的结果,相当于Promise里面的reject函数,一旦抛出错误,后面的代码就不会执行了
- 用
- 在普通函数前面加上
- await关键字:
- await后面一般跟Promise实例(其实也可以跟其他的数据,如果是其他的数据,则直接返回),await可以阻塞后续的代码,等待Promise实例返回结果后,再继续向下执行
- await可以直接拿到Promise实例的正确结果,错误结果需要用
try ... catch或catch(err => err)(推荐用这个)来获取 - await只能出现在异步函数(亲爸爸)中
范例:用async、await关键字改造依次读取1.txt、2.txt、3.txt文件的代码:
const fs = require('fs')
// promiseify可以将node中现有的基于回调函数形式的异步API,改造成返回Promise实例的函数
const { promiseify } = require('util')
// fs.readFile('./1.txt', 'utf8', (err, data) => { ... })
const readFileOfPromise = promiseify(fs.readFile)
(async () => {
const data1 = await readFileOfPromise('./1.txt', 'utf8')
console.log(data1)
const data2 = await readFileOfPromise('./2.txt', 'utf8')
console.log(data2)
const data3 = await readFileOfPromise('./3.txt', 'utf8')
console.log(data3)
})()
数据库概述:
- 数据库就是存储数据的仓库,可以对数据进行分类存储,并提供强大的操作数据的方法,是一个独立的软件
- 常用的数据库有:mysql、mongodb、orcle。。。
- mongodb是我们前端上手最快的数据库,我们先学习数据库相关的概念,日后再学习其他数据库时就会很轻松
数据库概念:
- 数据库(database):一个数据库软件中可以创建多个数据库,每个数据库都是独立的存储数据的仓库
- 集合(collection):集合是某个数据库中的一类数据的集合(可以理解为JS中的数组)[ {}, {}, {}, {} ]
- 文档(document):是集合中一条具体的数据(可以理解为数组中的对象){ name: 'zs', age: 18 }
- 字段(field):是文档中具体的某个属性(可以理解为数组中的对象的某个键值对)name: 'zs'
使用nodejs连接数据:
-
启动数据库:打开命令行,执行:
net start mongodb、mongod(Mac) -
停止数据库:打开命令行,执行:
net stop mongodb、mongod(Mac) -
使用第三方模块mongoose去操作数据库,范例:
const mongoose = require('mongoose') mongoose.connect('mongodb://localhost:27017/dbname').then(() => { console.log('数据库连接成功') }).catch(err => { console.log('数据库连接失败:', err) })补充说明:如果你连接的数据库不存在,mongodb会在第一次插入数据的时候自动给我们创建
创建集合及插入文档:
-
创建集合,分为两步:
- 创建集合规则
- 应用集合规则,创建集合
-
创建文档,分为两步:
new 集合()创建文档实例- 调用save方法保存
-
范例:
const mongoose = require('mongoose') mongoose .connect('mongodb://localhost:27017/test01') .then(() => { console.log('数据库连接成功') }) .catch(err => { console.log(err) }) const userSchema = new mongoose.Schema( { name: String, age: Number, isMarry: Boolean }, { versionKey: false } ) const User = mongoose.model( 'User' /* 是JS在运行时,内存中的集合名称,可以和数据库中真正的集合名称不一致,但是一般都是保持一致的 */, userSchema, 'User' /* 指定数据库里的集合名称,如果不指定,默认会改成小写的复数形式 */ ) const ls = new User({ name: 'ls', age: 18, isMarry: false }) ls.save() -
插入文件的另一种方式:可以使用集合构造函数的create方法来插入文档(推荐用这个),范例:
// [ 回调函数形式 User.create({ name: 'ww', age: 19, isMarry: true }, (err, user) => { cnosole.log(user) }) // ] 回调函数形式 // [ Promise 形式 User.create({ name: 'ww', age: 19, isMarry: true }).then(doc => { console.log(doc) }) // ] Promise 形式 // [ async await 形式 (推荐) ;(async () => { const ww = await User.create({ name: 'zl', age: 59, isMarry: true }) console.log(ww) })() // ] async await 形式
如果你写代码时,不喜欢加分号,那么在自执行函数前面是需要加分号的
向数据库中导入数据:
- 导入数据:
mongoimport -d 数据库名称 -c 集合名称 --file 要导入的文件路径 - 注意:mongodb安装目录的bin目录下存放的是mongodb相关的可执行文件,需要加入到path系统环境变量中,才阔以在命令行中执行命令
查询文档:
-
find():查找多个,返回的是数组,不管有多少个,返回的都是数组
-
findOne():查找一个,返回的是一个文档,如果匹配到多条,返回第一条
-
共同点:它们都返回Promise对象,既然返回的是Promise对象,那么就推荐使用async/await关键字去调用,都可以带查询条件
-
大于、小于:
User.find({ age: { $gt: 18, $lt: 40 } }).then(users => console.log(users)) -
包含:
User.find({ hobbies: { $in: ['敲代码'] } }).then(users => console.log(users)) -
筛选字段(_id会默认带上,如果不要在前面加个-):
User.find().select('name age').then(users => console.log(users)) -
排序:
// 升序 User.find().sort('age').then(users => console.log(users)) // 降序 User.find().sort('-age').then(users => console.log(users)) -
分页,跳过n条(skip),查n条(limit)
// 获取第4页的数据 User.find().skip(3 * 10).limit(10).then(users => console.log(users))
删除文档:
-
删除单个:
User.findOneAndDelete({ 条件 }).then...- 返回的是被删除的文档
- 如果匹配到多条,删除第一条
-
删除多个:
User.deleteMany({ 一定不要忘记带条件,如果不带就是无条件删除该集合中所有数据 }).then...- 返回值是本次删除操作的信息对象,形如:
{ n: 2, ok: 1, deletedCount: 2 }- n:表示匹配的条数
- ok:表示本次删除操作是否执行成功,1:成功,0:失败
- deletedCount:代表已经被删除的条数
- 补充说明:当本次删除操作异常时,n和deletedCount就有可能不一致
- 返回值是本次删除操作的信息对象,形如:
更新文档:
-
更新单个:
User.updateOne({ 条件 }, { 要更新的值 }).then...- 返回值是更新信息对象,形如:
{ n: 1, nModified: 1, ok: 1 }- n:表示匹配的条数
- nModified:表示已经更新的条数
- ok:表示本次更新操作是否执行成功,1:成功,0:失败
- 返回值是更新信息对象,形如:
-
更新多个:
User.updateMany({ 条件 }, { 要更新的值 }).then...- 返回值和updateOne一样