同步与异步
同步:程序代码一行一行的执行,只有上一行的代码执行完成才能继续执行下一行代码。 异步:不用等到上一行代码执行再执行下一行代码。比如程序里有一个2s的定时器,JS是一个单线程的运行机制,他不会在这干等2s,而是去执行其他事件。这就是JS异步高效性。
事件循环机制
JS运行机制里会有一个主线程,宏任务队列,微任务队列。 事件循环机制:
- 将同步宏任务(打印、promise里回调函数…)加入主线程中
- 执行完主线程中的任务,将微任务队列加入主线程中
- 再次执行主线程中的任务(异步微任务),执行完将宏任务队列加入主线程中
- 再次执行主线程中的任务(异步宏任务),执行完跳到第2步,不断重复
异步宏任务:setTimeout、setInterval… 异步微任务:promise.then()
总结:事件循环机制为:同步宏任务 -> 异步微任务 -> 异步宏任务 -> 异步微任务 -> 异步宏任务 ->…
回调地狱
回调地狱:例如接口A需要接口B的回调res数据去请求数据,如果嵌套多层,就会产生回调地狱。
setTimeout(() => {
console.log('1')
setTimeout(() => {
console.log('2')
setTimeout(() => {
console.log('3')
console.log('...')
}, 1000)
}, 1000)
}, 1000)
这样写即显得可读性不好,又显得维护性不好。就这样,出现了ES6里的Promise来解决回调地狱问题。
Promise
Promise的三种状态:pending(未确认)、fulfilled(成功)、rejected(失败)。 Promise的状态改变只能从:pending -> fulfilled 或者是 pending -> rejected。
let p = new Promise((resolve, reject) => {
console.log(222);
resolve(1)
})
p.then((res) => {
setTimeout(() => {
console.log(res)
}, 1000);
return res + 1
}).then((res) => {
setTimeout(() => {
console.log(res)
}, 2000);
return res + 1
}).then((res) => {
setTimeout(() => {
console.log(res)
}, 3000);
})
创建Promise对象,里面接收一个resolve和reject两个参数的回调函数,分别代表成功和失败状态。在定义的同时会直接执行回调函数里的代码,然后使用.then 的方式获取resolve的参数,使用.catch 获取reject里的参数。执行完.then 或者是.catch 会返回一个Promise对象,就可以继续.then 了。这样不仅解决了回调地狱的问题,还显得代码可读性好,易于维护。但是,Promise当处于Pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成),而且它一旦创建了,就无法取消了。后来就出现了Generator。
Generator
Generator 函数是一个状态机,封装了多个内部状态。执行 Generator 函数会返回一个遍历器对象,可以依次遍历 Generator 函数内部的每一个状态,但是只有调用next 方法才会遍历下一个内部状态,所以其实提供了一种可以暂停执行的函数。yield 表达式就是暂停标志。
function* helloWorldGenerator() {
yield 'hello';
yield 'world';
return 'ending';
}
var hw = helloWorldGenerator();
hw.next()
hw.next()
hw.next()
hw.next()
由结果可以看出,Generator函数 被调用时并不会执行,只有当调用next方法 、内部指针指向该语句时才会执行,即函数可以暂停,也可以恢复执行 。每次调用遍历器对象的next方法,就会返回一个有着value 和done 两个属性的对象。value 属性表示当前的内部状态的值,是yield表达式后面那个表达式的值;done 属性是一个布尔值,表示是否遍历结束。
Generator 函数暂停恢复执行原理:使用协程的思想( 一个线程(或函数)执行到一半,可以暂停执行,将执行权交给另一个线程(或函数),等到稍后收回执行权的时候,再恢复执行。这种可以并行执行、交换执行权的线程(或函数),就称为协程。 )。
Async/Await
async 是Generator函数的语法糖 。 async函数就是将 Generator 函数的星号(*)替换成async,将yield替换成await 。 返回值是 Promise。 async 函数返回值是 Promise 对象,比 Generator 函数返回的 Iterator 对象方便,可以直接使用 then() 方法进行调用。 分析下面两道题:
console.log('script start')
async function async1() {
await async2()
console.log('async1 end')
}
async function async2() {
console.log('async2 end')
}
async1()
setTimeout(function() {
console.log('setTimeout')
}, 0)
new Promise(resolve => {
console.log('Promise')
resolve()
})
.then(function() {
console.log('promise1')
})
.then(function() {
console.log('promise2')
})
console.log('script end')
console.log('script start')
async function async1() {
await async2()
console.log('async1 end')
}
async function async2() {
console.log('async2 end')
return Promise.resolve().then(()=>{
console.log('async2 end1')
})
}
async1()
setTimeout(function() {
console.log('setTimeout')
}, 0)
new Promise(resolve => {
console.log('Promise')
resolve()
})
.then(function() {
console.log('promise1')
})
.then(function() {
console.log('promise2')
})
console.log('script end')
async函数返回的是一个promise对象,如果在函数中 return 一个直接量,async 会把这个直接量通过 Promise.resolve() 封装成 Promise 对象。await相当于Promise的then。
await后面的函数会先执行一遍,然后就会跳出整个async函数来执行后面js栈的代码。等本轮事件循环执行完了之后又会跳回到async函数中等待await。后面表达式的返回值,如果返回值为非promise则继续执行async函数后面的代码,否则将返回的promise放入微任务队列。
|