进程与线程
- 进程(厂房)
- 程序的运行环境
- 线程(工人)
- 线程是实际进行运算的东西
同步与异步
同步的特点
- 通常情况代码都是自上向下一行一行执行的
- 前边的代码不执行后边的代码也不会执行
- 同步的代码执行会出现阻塞的情况
- 一行代码执行慢会影响整个程序的执行
function sum(a, b){
let begin = Date.now()
while(Date.now() - begin < 10000){
}
return a + b
}
console.log("第一行打印")
let result = sum(123, 456)
console.log(result)
console.log("第二行打印")
上面的代码sum()执行会停顿10秒,10秒后才会返回结果。由于是同步代码,所以sum()会阻塞其后所有代码的执行,导致整个程序的执行速度极差。
如何解决同步阻塞的问题
node.js通过异步来解决
异步的特点
- 不会阻塞其他代码的执行
- 需要通过回调函数来返回结果
- 异步的问题
- 异步的代码无法通过return来设置返回值
基于回调函数的异步带来的问题
- 代码的可读性差
- 可调试性差
回调地狱
function sum (a, b, cb) {
setTimeout(() => {
cb(a + b)
}, 1000)
}
console.log('111111')
sum(123, 456, (result) => {
sum(result, 7, (result) => {
sum(result, 8, result => {
sum(result, 9, result => {
sum(result, 10, result => {
console.log(result)
})
})
})
})
})
console.log('222222')
假设需要调5次异步函数,后一个函数要依赖前一个的运算结果,这样就出现了回调地狱,也就是死亡金字塔,这样既不好维护,也增加了代码的复杂程度,变得难以理解。
解决问题
- 需要一个东西,可以代替回调函数来给我们返回结果
- Promise横空出世 - Promise是一个可以用来存储数据的对象,Promise存储数据的方式比较特殊,这种特殊方式使得Promise可以 用来存储异步调用的数据
Promise
- Promise可以帮助我们解决异步中的回调函数的问题
- Promise就是一个用来存储数据的容器 它拥有着一套特殊的存取数据的方式 这个方式使得它里面可以存储异步调用的结果
创建Promise
- 创建Promise时,构造函数中需要一个函数作为参数
- Promise构造函数的回调函数,他会在创建Promise时调用,调用时会有两个参数传递进去
const promise = new Promise((resolve,reject)=>{
// resolve 和reject是两个函数,通过这两个函数可以向Promise中存储数据
// resolve在执行正常时存储数据,reject在执行错误时存储数据
// 通过函数来向Promise中添加数据,好处就是可以用来添加异步调用的数据
resolve("哈哈")
})
从Promise中读取数据
- 可以通过Promise的实例方法then来读取Promise中存储的数据
- then需要两个回调函数作为参数,回调函数用来获取Promise中的数据
- 通过resolve存储的数据,会调用第一个函数返回,可以在第一个函数中编写处理数据的代码
- 通过reject存储的数据或者出现异常时,会调用第二个函数返回可以在第二个函数中编写处理异常的代码
promise.then((result) => {
console.log('1', result)
}, (reason) => {
console.log('2', reason)
})
Promise中维护的两个隐藏属性
- PromiseResult
- 用来存储数据
- PromiseState
- 记录Promise的状态(三种状态)
- pending(进行中)
- fulfilled(完成) 通过resolve存储数据时
- rejected(拒绝,出错了) 出错了或者通过reject存储数据时
- state只能修改一次,修改以后永远不能再变
- 记录Promise的状态(三种状态)
- 流程
- 当Promise创建时,PromiseState初始值为pending
- 当通过resolve存储数据时 PromiseState 变为fulfilled(完成) PromiseResult变为存储的数据
- 当通过reject存储数据或出错时 PromiseState 变为rejected(拒绝,出错了) PromiseResult变为存储的数据 或 异常对象
- 当我们通过then读取数据时,相当于为Promise设置了回调函数
- 如果PromiseState变为fulfilled,则调用then的第一个回调函数来返回数据
- 如果PromiseState变为rejected,则调用then的第二个回调函数来返回数据
- catch()
- 用法和then类似,但是只需要一个回调函数作为参数
- catch()中的回调函数只会在Promise被拒绝时才会调用
- catch()相当于then(null,reason=>{})
- catch()就是一个专门处理Promise异常的方法
- finally()
- 无论是正常存储数据还是出现异常了,finally总会执行
- 但是finally的回调函数中不会接收到数据
- finally()通常用来编写一些无论成功与否都要执行的代码
- 当Promise创建时,PromiseState初始值为pending
const promise2 = new Promise((resolve, reject) => {
// resolve('哈哈')
reject('哈哈')
})
promise2.then(result => {
console.log(result)
}, reason => {
console.log('出错了')
})
promise2.catch(reason => {
console.log(222222)
})
promise2.finally(() => {
console.log('没有什么能够阻挡我执行的')
})
Promise的链式调用
对Promise进行链式调用时,后边的方法(then和catch)读取的上一步的执行结果,如果上一步的执行结果不是当前想要的结果,则跳过当前的方法。当Promise出现异常时,而整个调用链中没有出现catch,则异常会向外抛出。
promise
.then(r => console.log("第一个then", r))
.catch((r) => {
throw new Error("报个错玩")
})
.then(r => console.log("第二个then", r))
.catch(r => {
console.log("出错了")
}) //打印:出错了
Promise中的
- then
- catch
- Promise中会存储回调函数的返回值
- 这三个方法都会返回一个新的Promise
- finally
- finally的返回值,不会存储到新的Promise中
const promise = new Promise((resolve, reject) => {
// reject('周一到周五19点,不见不散')
resolve('哈哈')
})
promise
.then(result => {
console.log('回调函数', result)
return '锄禾日当午'
})
.then(result => {
console.log("第二个then", result)
return "超哥真快乐"
})
.then(result => {
console.log(result)
})
//打印:回调函数 哈哈
第二个then 锄禾日当午
超哥真快乐
用Promise改写之前的回调地狱
function sum(a,b){
return new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve(a+b)
},1000)
})
}
sum(123,456)
.then(result => result+7)
.then(result => result+8)
.then(result => result+9)
Promise的静态方法
- Promise.resolve() 创建一个立即完成的Promise
Promise.resolve(10).then(r => {
console.log(r)
})
- Promise.reject() 创建一个立即拒绝的Promise
Promise.reject("错误")
- Promise.all([...]) 同时返回多个Promise的执行结果,有一个报错,就返回错误
Promise.all([
sum(123, 456),
sum(5, 6),
// Promise.reject('哈哈'),
sum(33, 44)
]).then(r => {
console.log(r)
})
- Promise.allSettled([...]) 同时返回多个Promise的执行结果(无论成功或失败)
Promise.allSettled([
sum(123, 456),
sum(5, 6),
Promise.reject('哈哈'),
sum(33, 44)
]).then(r => {
console.log(r)
})
- Promise.race([...])返回执行最快的Promise(不考虑对错)
Promise.race([
Promise.reject(1111),
sum(123, 456),
sum(5, 6),
sum(33, 44)
]).then(r => {
console.log(r)
}).catch(r => {
console.log('错误')
})
- Promise.any([...])返回执行最快的完成的Promise
Promise.any([
Promise.reject(1111),
Promise.reject(2222),
Promise.reject(3333),
]).then(r => {
console.log(r)
}).catch(r => {
console.log('错误', r)
})