这是我参与8月更文挑战的第14天,活动详情查看:8月更文挑战
javascript 异步操作执行历史
JavaScript 语言的执行环境是“单线程”, 所谓单线程,就是一次只能完成一件任务, 如果有多个任务就需要排队,一个完成了,继续下一个,这种方式在实现来说是非常简单的,但是如果一个任务耗时很长,那么后面的任务就需要排队等着,会拖延整个程序的执行。 常见的浏览器无响应(假死)就是因为某一段 JavaScript 代码长时间运行(比如死循环),导致整个页面卡死,其他任务无法执行。
javascript 异步操作的类型
- 回调函数
- 事件监听
- 发布/订阅
- promise
- generator(ES6)
- async/await (ES7)
回调函数
- 同步回调 立即执行,完全执行完之后才结束,不会放入回调队列 example : Promise(()=>{}) 数组的 forEach()
- 异步回调 setTimeout((()=>{}))
var arr = [1, 1, 1]
arr.forEach((value) => {
console.log(value)
})
console.log('over')
-
异步回调 进入回调队列,等待执行
-
回调函数
function f1(func) {
setTimeout(() => {
console.log('我是f1')
for (let i = 1; i < 50000; i++) {
console.log('我是f1的' + i)
}
func(Zz)
}, 500)
}
function f2() {
alert('我是f2')
}
function f3() {
alert('我是f3')
}
f1(f2) //这里f1(),f2()不会阻塞f3()的运行
f3()
这里主要利用setTimeout()函数来进行异步操作,f3()的执行不会受到f1()影响。主要是因为setTimeout()是异步函数。
- 为什么要用 Promise
- 1.指定回调函数的而方式更加灵活
- 2.支持链式调用,可解决回调地狱问题 回调地狱就是传统的回调函数嵌套所产生的问题,不利于阅读,不利于异常处理,解决方案有 Promise 链式调用以及最新的 async 和 await
Promise
- Promise 是 ES6 中提出的新的异步编程解决方案 语法上来看:Promise 是一个构造函数 功能上来看;Promise 对象用来封装了一个异步操作并可以获取其结果
- Promise 状态 pending 状态-->初始状态 fullFilled 状态-->对应 Promise 内函数执行成功-->对应 resolve(params) rejected 状态-->对应 Promise 内函数执行失败或抛出异常-->对应 reject(params)
- Promise 执行流程
如何改变 promise 的状态
resolve(value)//---pedding->fullfilled
reject(reason)//---pedding->rejected
throw //抛出异常,rejected,reason为throw的值
定义两个 then(),都会输出
promise 状态的改变和 then 回调函数的执行
//先指定回调函数,在改变状态
new Promise((resolve, reject) => {
//这里是同步执行
console.log`先指定回调函数,在改变状态`
setTimeout(() => {
resolve(1) //再改变pedding状态,同时改变数据
}, 1000)
}).then((value) => {
//先指定回调函数
console.log('value1---->' + value)
})
//先改变状态,再指定函数
//第一种,去掉异步的定时器
new Promise((resolve, reject) => {
console.log`//先改变状态,再指定函数`
resolve(2)
}).then((value) => {
console.log('value2--->' + value)
})
//第二种都设置定时器
const p3 = new Promise((resolve, reject) => {
console.log`//先改变状态,再指定函数`
setTimeout(() => {
resolve(3)
}, 1000)
})
setTimeout(() => {
p3.then((value) => {
console.log('value3--->' + value)
})
}, 1200)
/*输出:
[ '先指定回调函数,在改变状态' ]
[ '//先改变状态,再指定函数' ]
[ '//先改变状态,再指定函数' ]
value2--->2
value1---->1
value3--->3
*/
Promise.all() 和 Promise.race()
- Promise.all() Promise.all(iterable) 方法返回一个 Promise 实例,此实例在 iterable 参数内所有的 promise 都“完成(resolved)”或参数中不包含 promise 时回调完成(resolve);如果参数中 promise 有一个失败(rejected),此实例回调失败(reject),失败的原因是第一个失败 promise 的结果。
- Promise.race() Promise.race(iterable) 方法返回一个 promise,一旦迭代器中的某个 promise 解决或拒绝,返回的 promise 就会解决或拒绝。
Promise 值穿透问题
- .then 或者 .catch 的参数期望是函数,传入非函数则会发生值穿透。
Promise.resolve('foo')
.then(Promise.resolve('bar'))
.then(function (result) {
console.log(result) //foo
})
//-----------------------------
Promise.resolve(1)
.then(function () {
return 2
})
.then(Promise.resolve(3))
.then(console.log) //2
- 异常穿透 (1)当使用 promise 的 then 链式调用时, 可以在最后指定失败的回调, (2)前面任何操作出了异常, 都会传到最后失败的回调中处理
中断 promise 链
(1)当使用 promise 的 then 链式调用时, 在中间中断, 不再调用后面的回调函数 (2)办法: 在回调函数中返回一个 pendding 状态的 promise 对象
手写 Promise 对象
已上传到 GIthub 手写 Promise(ES5).
async 和 await
- async 函数 函数的返回值为 Promise 对象 Promise 对象的结果由 async 函数执行的返回值决定
- await 函数 await 右侧的表达式一般为 Promise 对象,但也可以是其他值 如果表达式是 Promise 对象,await 返回 Promise 成功的值 如果为其他值,则直接作为 await 的返回值
- 注意 await 必须写在 async 函数中,但 async 函数中可以没有 await await 的 Promise 对象失败,会抛出异常,用 try...catch 捕获异常 🍎
宏队列 和 微队列
- 宏队列 DOM 事件回调 Ajax 回调 定时器回调
- 微队列
Mutation() Promise()
<script> //微队列优先级高 setTimeout(() => { //立即进入宏队列 console.log('timeout1') Promise.resolve(2).then((value) => { console.log('setTimeout的Promise1' + value) }) }, 0) setTimeout(() => { //立即进入宏队列 console.log('timeout2') /* 每次准备取出一个宏队列的任务时,都会先执行所有的微任务 */ Promise.resolve(2).then((value) => { console.log('setTimeout的Promise2' + value) }) }, 0) Promise.resolve(1).then((value) => { //立即进入微队列 console.log('Promise onResolved' + value) }) //Promise onResolved 1. //timeout </script>