文章目录
一、回调函数的同步与异步
1、同步回调函数
const arr = [1, 3, 5]
arr.forEach(item => { // 遍历回调, 同步回调函数, 不会放入列队, 一上来就要执行完
console.log(item)
})
console.log('forEach()之后')
2、异步回调函数
setTimeout(() => { // 异步回调函数, 会放入队列中将来执行
console.log('timout callback()')
}, 0)
console.log('setTimeout()之后')
二、常见错误
1、错误的类型
1、错误的类型
Error: 所有错误的父类型
ReferenceError: 引用的变量不存在
TypeError: 数据类型不正确的错误
RangeError: 数据值不在其所允许的范围内
SyntaxError: 语法错误
2、错误处理
捕获错误: try … catch 为了捕获到错误之后还可以继续进行后面的代码
抛出错误: throw error
3、错误对象
message属性: 错误相关信息
stack属性: 函数调用栈记录信息
三、Promise流程
四、为什么要用Promise
1、指定回调函数的方式更加灵活:
旧的: 必须在启动异步任务前指定
promise: 启动异步任务 => 返回promie对象 => 给promise对象绑定回调函数(甚至可以在异步任务结束指定)
- 也就是说在异步任务开始之前回调函数就得写好,不能说已经结束了以后才去获取回调的值,那时候已经获取不到了,而promise则可以在任何时候都可以绑定回调函数
2、解决回调地狱的问题
- 回调地狱:进行多个串联的异步函数时,当前的异步函数需要上一层异步函数的回调函数的结果作为条件,一层层嵌套
- promise是通过.then方法从上到下,结构清晰,而且不需要写失败的回调,只要写一个.catch即可获得中间任意一个失败的回调(异常传透),但是他还存在回调函数,不是纯碎的同步编码方式
- 最优解:async / await ,没有回调函数,同步编码方式
五、Promise的一些API
1、Promise构造函数: Promise (excutor) {}
excutor函数: 同步执行 (resolve, reject) => {}
resolve函数: 内部定义成功时我们调用的函数 value => {}
reject函数: 内部定义失败时我们调用的函数 reason => {}
说明: excutor会在Promise内部立即同步回调,异步操作在执行器中执行
2、Promise.prototype.then方法: (onResolved, onRejected) => {}
onResolved函数: 成功的回调函数 (value) => {}
onRejected函数: 失败的回调函数 (reason) => {}
说明: 指定用于得到成功value的成功回调和用于得到失败reason的失败回调
返回一个新的promise对象
3、Promise.prototype.catch方法: (onRejected) => {}
onRejected函数: 失败的回调函数 (reason) => {}
说明: then()的语法糖, 相当于: then(undefined, onRejected)
4、Promise.resolve方法: (value) => {}
value: 成功的数据或promise对象
说明: 返回一个成功/失败的promise对象
5、Promise.reject方法: (reason) => {}
reason: 失败的原因
说明: 返回一个失败的promise对象
6、Promise.all方法: (promises) => {}
promises: 包含n个promise的数组
说明: 返回一个新的promise, 只有所有的promise都成功才成功, 只要有一个失败了就直接失败
7、Promise.race方法: (promises) => {}
promises: 包含n个promise的数组
说明: 返回一个新的promise, 第一个完成的promise的结果状态就是最终的结果状态
六、Promise的回调函数
1、不管是成功的回调函数还是失败的回调函数,都是异步的,即使条件满足了也不是立即执行
2、 .then返回的是一个新的Promise对象
3、 .then后面还可以.then,但是后面的.then的回调函数的结果得依靠前面的.then的Promise对象return的值,如果没有return,那就是默认undefined (重点面试题)
new Promise((resolve, reject) => {
resolve(1)
}).then(
value => { console.log('onResolved1', value) },
reason => { console.log('onRejected1', reason) }
).then(
value => { console.log('onResolved2', value) },
reason => { console.log('onRejected2', reason) }
)
console.log('***')
// *** // 这个最先打印,说明.then里面的回调函数都是异步的
// onResolved1 1
// onResolved2 undefined
new Promise((resolve, reject) => {
resolve(1)
}).then(
value => {
console.log('onResolved1', value)
return 2 // return Promise.resolve(3) // return Promise.reject(4) // throw 5
},
reason => { console.log('onRejected1', reason) }
).then(
value => { console.log('onResolved2', value) },
reason => { console.log('onRejected2', reason) }
)
// onResolved1 1
// onResolved2 2 // onResolved2 3 // onRejected2 4 // onRejected2 5
new Promise((resolve, reject) => {
reject(1)
}).then(
value => { console.log('onResolved1', value) },
reason => {
console.log('onRejected1', reason)
// throw 5
}
).then(
value => { console.log('onResolved2', value) },
reason => { console.log('onRejected2', reason) }
)
// onRejected1 1
// onResolved2 undefined // onRejected2 5
// 也就是说上一次的.then是不是成功的回调不影响下一次.then的回调结果,只要不是throw,那么下一个.then就是成功的回调onResolved(),没有return值的话成功的回调值就是undefined
4、如果想要在.then里面再写一个异步操作,必须得包在new promise里面,然后return出去
new Promise((resolve, reject) => {
setTimeout(() => {
console.log("执行任务1(异步)")
resolve(1)
}, 1000);
}).then(
value => {
console.log('任务1的结果: ', value)
console.log('执行任务2(同步)')
return 2
}
).then(
value => {
console.log('任务2的结果:', value)
return new Promise((resolve, reject) => {
// 启动任务3(异步)
setTimeout(() => {
console.log('执行任务3(异步))')
resolve(3)
}, 1000);
})
}
).then(
value => {
console.log('任务3的结果: ', value)
}
)
5、异常传透(重点面试题)
new Promise((resolve, reject) => {
reject(1)
}).then(
value => { console.log('onResolved1', value) },
// reason => { throw reason }
// reason => Promise.reject(reason)
).then(
value => { console.log('onResolved2', value) },
// reason => { throw reason }
).then(
value => { console.log('onResolved3', value) },
// reason => { throw reason }
).catch(
reason => { console.log('onRejected', reason) }
)
// 相当于每一个.then里面都加了一个 reason => { throw reason } 或者 reason => Promise.reject(reason) 可加可不加
// 如果中间哪一个成功回调了,底下的.catch就不走了
6、中断promise链(重点面试题)
- 返回一个panding的promise,这样就没有resolved也没有rejected状态了,那么后面依赖这个状态的.then就一直不动了,这样就实现了中断promise链
- .catch返回的也是一个Promise对象,也可以.then
new Promise((resolve, reject) => {
reject(1)
}).then(
value => { console.log('onResolved1', value) },
reason => { throw reason }
).then(
value => { console.log('onResolved2', value) },
).then(
value => { console.log('onResolved3', value) },
).catch(
reason => {
console.log('onRejected', reason)
return new Promise(()=>{}) // 返回一个panding的promise
}
).then(
value => { console.log('onResolved4', value) },
reason => { console.log('onRejected4', reason) }
)
七、async await
1、async 函数
- 函数的返回值为promise对象
- promise对象的结果由async函数执行的返回值决定
async function fn1() {
return 1 // 相当于 return Promise.resolve(1)
// throw 1 // 相当于 return Promise.reject(1)
}
const result = fn1()
result.then(
value => {
console.log('onResolved', value)
},
reason => {
console.log('onRejected', reason)
}
)
// onResolved 1
2、await 表达式
- await右侧的表达式一般为promise对象, 但也可以是其它的值
- 如果表达式是promise对象, await返回的是promise成功的值value
- 如果表达式是其它值, 直接将此值作为await的返回值
function fn1() {
return Promise.resolve(1)
}
// 用到await 必须加上async
async function fn2() {
const result = await fn1() // 使用await就可以不用.then而直接获取promise成功的value
console.log(result); // 1
}
fn2()
3、async await的注意点
- await必须写在async函数中, 但async函数中可以没有await
- await只能获得promise成功的结果value,如果await的promise失败了, 就会抛出异常, 需要通过try…catch来捕获处理,并且try…catch也得在async里面
function fn1() {
return Promise.reject(1)
}
// 用到await 必须加上async
async function fn2() {
const result = await fn1()
console.log(result);
}
fn2()
// 此时就会报错,因为await只能获得成功回调的结果
// 如果promise的值是失败的回调,那么await会报错,只能用try...catch来获得promise失败的值
function fn1() {
return Promise.reject(2)
// throw 2
}
// 用到await 必须加上async
async function fn2() {
try {
const result = await fn1()
console.log('onResolved()', result)
} catch (error) {
console.log('onRejected()', error) // onRejected() 2
}
}
fn2()
八、JS异步之宏队列与微队列
1、js中用来存储待执行的回调函数的队列包含2个不同特定的队列
2、宏列队: 用来保存待执行的宏任务(回调), 比如: 定时器回调/DOM事件回调/ajax回调
3、微列队: 用来保存待执行的微任务(回调), 比如: promise的回调/MutationObserver的回调
4、JS执行时会区别这2个队列
- JS引擎首先必须先执行所有的初始化同步任务代码
- 每次准备取出第一个宏任务执行前, 都要将所有的微任务一个一个取出来执行
- 同步任务 > 微队列 > 宏队列