事件循环
JavaScript
是一门单线程的编程语言,所以没有并发并行等特性。
为了协调事件、用户交互、脚本、UI 渲染和网络处理等行为,防止主线程的不阻塞,(事件循环)Event Loop
的方案应用而生。
JavaScript
处理任务是在等待任务、执行任务 、休眠等待新任务中不断循环中,也称这种机制为事件循环。
主线程中的任务执行完后,才执行任务队列中的任务
有新任务到来时会将其放入队列,采取先进先执行的策略执行队列中的任务
比如多个
setTimeout
同时到时间了,就要依次执行
任务包括 script
(整体代码)、 setTimeout
、setInterval
、DOM渲染、DOM事件、Promise
、XMLHTTPREQUEST
等
任务详解
任务分类
任务大致分为以下三种:
主线程任务
应放入宏队列中的任务
应放入微队列中的任务
放入宏队列中的任务 | ||
---|---|---|
# | 浏览器 | Node |
setTimeout | √ | √ |
setInterval | √ | √ |
setImmediate | x | √ |
requestAnimationFrame | √ | x |
放入微队列中的任务 | ||
---|---|---|
# | 浏览器 | Node |
process.nextTick | x | √ |
MutationObserver | √ | x |
Promise.then catch finally | √ | √ |
执行顺序
根据任务的不同,执行顺序也有所不同:
1.主线程任务
2.微队列任务
3.宏队列任务
作用体现
使用Promise
能让代码变得更易阅读,方便后期维护。
特别是在回调函数嵌套上,更应该使用Promise
来书写代码。
嵌套问题
以下示例将展示通过 代码逻辑虽然清晰但是定时器回调函数嵌套太过复杂,阅读体验较差。 使用 这里看不懂没关系,下面会慢慢进行剖析,只是感受一下是不是嵌套没那么严重了看起来好看多了。 可以通过链式调用多个 每一个 此外,每一个 当一个 当没有使用 使用 使用 在一个 每个 建议使用 将 错误是冒泡操作的,下面没有任何一个 无论状态是 使用 其实每一个 此时就会产生一种链式关系,每一个 要想使用链式调用,一定要搞明白每一个 返回了一个值,那么 没有返回任何值,那么 抛出一个错误,那么 返回一个已经是接受状态的 返回一个已经是拒绝状态的 返回一个未定状态( 上一个 下一个 上一个 下一个 上一个 我们可以利用在一个 继续对上面的代码做优化。 使用 的 使用 使用 任何一个 适用于一次发送多个异步操作 参数必须是可迭代类型,如 成功后返回 以下示例将展示同时提交两个异步操作,只有当全部成功时才会执行 使用 其实这在某些资源引用上比较常用,可以添加多个资源地址进行请求,谁先快就用谁的。 以最快返回的 如果最快返加的状态为 如果参数不是 下面示例中成功1比较快,就用成功1的。 使用 在某一个函数前加上 我们可以依照标准 使用 一般 当一个 如果在 更推荐写成下面这种形式 也可使用Js
来使得
尝试解决
Promise
来解决该问题。
Promise
JavaScript
中存在很多异步操作,Promise
将异步操作队列化,按照期望的顺序执行,返回符合预期的结果。Promise
达到我们的目的,如同上面示例一样会让代码可读性大幅度提升。声明状态
Promise
对象都接收一个函数,该函数需要提供两个参数,分别是resolve
以及reject
,代表当前函数中的任务成功与失败,这是属于线程任务的,所以会优先执行。Promise
对象都具有三种状态,分别是pending
,fulfilled
,rejected
。Promise
对象状态改变过后,将不能再次改变。
pending
指初始等待状态,初始化 promise
时的状态 resolve
指已经解决,将 promise
状态设置为fulfilled
reject
指拒绝处理或未解决,将 promise
状态设置为rejected
resolve
或 reject
更改状态时,状态为 pending
resolve
修改状态后,状态为fulfilled
reject
修改状态后,状态为rejected
then
Promise
对象状态为resolve
或reject
时,可以紧跟then
方法,该方法可接收两个个函数对象,用于处理Promise
对象reject
或resolve
传递过来的值。
catch
then
都可以指定第二个函数用于处理上一个Promise
失败的情况,如果每个then
都进行这样设置会显得很麻烦,所以我们只需要使用catch
即可。catch
可以捕获之前所有 promise
的错误,所以建议将 catch
放在最后。
catch
处理错误catch
放在最后面用于统一处理前面发生的错误then
定义第二个函数,将一直冒泡到 catch
处理错误
catch
也可捕捉到throw
自动触发的异常。
finally
fulfilled
或rejected
都会执行此动作,finally
与状态无关。
链式调用
Promise
进行链式调用,可以规避掉嵌套问题。基本概念
then
都是一个新的Promise
,默认返回为fulfilled
状态。then
都是一个新的Promise
对象,而每个then
的作用又都是处理上个Promise
对象的状态。then
的返回值。
then
返回的 Promise
将会成为接受状态,并且将返回的值作为接受状态的回调函数的参数值。then
返回的 Promise
将会成为接受状态,并且该接受状态的回调函数的参数值为 undefined
。then
返回的 Promise
将会成为拒绝状态,并且将抛出的错误作为拒绝状态的回调函数的参数值。Promise
,那么 then
返回的 Promise
也会成为接受状态,并且将那个 Promise
的接受状态的回调函数的参数值作为该被返回的Promise
的接受状态回调函数的参数值。Promise
,那么 then
返回的 Promise
也会成为拒绝状态,并且将那个 Promise
的拒绝状态的回调函数的参数值作为该被返回的Promise
的拒绝状态回调函数的参数值。pending
)的 Promise
,那么 then
返回 Promise 的状态也是未定的,并且它的终态与那个 Promise 的终态相同;同时,它变为终态时调用的回调函数参数与那个 Promise 变为终态时的回调函数的参数是相同的。无返回
then
无返回值时该then
创建的Promise
对象为fulfilled
状态。then
会立即执行,接收值为undefined
。
返回值
then
有返回值时该then
创建的Promise
对象为fulfilled
状态。then
会立即执行,接收值为上一个then
的返回值。
返回Promise
then
有返回值且该返回值是一个Promise
对象的话下一个then
会等待该Promise
对象状态改变后再进行执行,接收值根据被返回的Promise
对象的任务处理状态来决定。
嵌套解决
then
中返回Promise
下面的then
会等待状态的特性,对定时器回调函数嵌套进行优化。
代码优化
扩展接口
resolve
Promise.resolve()
方法可以快速的返回一个状态是fulfilled
Promise
对象。
reject
Promise.reject()
方法可以快速的返回一个状态是reject
的Promise
对象。
all
Promise.all()
方法可以同时执行多个并行异步操作,比如页面加载时同进获取课程列表与推荐课程。
Promise
执行失败就会调用 catch
方法Array/Set
Promise
结果的有序数组Promise.all()
其下的then
allSettled
allSettled
用于处理多个Promise
,只关注执行完成,不关注是否全部执行成功,allSettled
状态只会是fulfilled
。
race
Promise.race()
处理容错异步,和race
单词一样哪个Promise
快用哪个,哪个先返回用哪个。
Promise
为准rejected
那整个Promise
为rejected
执行cache
Promise
,内部将自动转为Promise
async/await
async/await
是Promise
的语法糖,可以让编写 Promise
更清晰易懂,也是推荐编写Promise
的方式。
async/await
本质还是Promise
,只是更简洁的语法糖书写async
async
,该函数会返回一个Promise
对象。Promise
来操纵该对象。
await
await
关键词后会等待Promise
完。
await
后面一般是Promise
,如果不是直接返回await
必须放在 async
定义的函数中使用await
用于替代 then
使编码更优雅
await
后面是外部其它的Promise
对象
异常处理
Promise
状态为rejected
其实我们就可以将它归为出现异常了。await
发生异常时,其他的await
不会进行执行。
async
中不确定会不会抛出异常,我们可以在接收时使用catch
进行处理。
try...catch
进行处理。