导入例子
同步阻塞:老王用水壶煮水,站在那里看水什么时候开
同步非阻塞:老王用水壶煮水,跑到客厅看电视,每隔一段时间过来看看水开没
异步阻塞:老王用响水壶煮水,站在那里等着响水壶通知自己水开
异步非阻塞:老王用响水壶煮水,跑到客厅看电视,等待响水壶通知自己水开
同步与异步:同步一般指主动请求并等待I/O操作完毕的方式。异步一般指主动请求后可以继续处理其他任务,随后等待I/O操作完毕的通知(响水壶)
阻塞和非阻塞:讨论的角度是线程是否被阻塞,非阻塞就是煮水的过程中无法去干别的事,非阻塞就是在同样的情况下可以去干别的事
详解异步
异步的出现是为了解决同步阻塞的问题,也就是一行一行执行指令的,当遇到某个指令需要耗费大量时间的时候,后面的指令就无法执行,这种同步执行的操作容易使页面呈卡死状态
callback、Ajax、Promise、async await、nextTick()
不进入主线程、而进入"任务队列"的任务,只有等主线程任务执行完毕,"任务队列"开始通知主线程,请求执行任务,该任务才会进入主线程执行
JS队列 & 执行优先级
- 主线程:JS只有一个线程,这个线程负责解释和执行JS代码,称其为主线程,在这个主线程上,所有的代码按照顺序执行
- 消息队列:消息队列里面放的是一些待触发执行的方法,如点击事件,当触发之后,会被通知主线程去执行
- 任务队列:同步代码放到执行栈中执行,异步代码会先存放到任务队列中,执行栈代码先执行,异步代码在执行栈执行完后再执行。任务队列优先级高于消息队列
- 延迟函数执行(setTimeout、setInterval):等到执行栈清空的时候才会执行
异步任务中又分为宏任务和微任务两种
- 微任务(Promise、async/await、process.nextTick):执行完当前主线程任务后就要马上执行的任务
- 宏任务(setTimeout、setInterval、Ajax、事件绑定):可以理解是每次执行栈执行的代码就是一个宏任务
Js不存在异步,可以通过事件轮询实现异步,是单线程的。
JS的宿主环境(浏览器,node)存在异步,是多线程的。
优先级:同步>异步(任务队列>消息队列)>延迟函数
先同步后异步,异步分宏任务、微任务,同级先微后宏
- new Promise是一个构造函数,是同步任务
- Promise.resolve().then 是微任务
- async是同步任务
- await方法返回的是一个Promise对象,后面相当于Promise then, 是微任务
- 定时器,加入下一次宏任务,是宏任务
Promise
- Promise的创建和初始化是同步的(本身是构造函数)
- Promise的回调(通过then、catch、finally等方法)是异步执行的
const myPromise = new Promise((resolve, reject) => {
setTimeout(() => {
const randomNumber = Math.random();
if (randomNumber > 0.5) {
resolve(randomNumber);
} else {
reject("Error: Number is too small");
}
}, 1000);
});
myPromise
.then((result) => {
console.log("Success:", result);
})
.catch((error) => {
console.log("Error:", error);
});
创建Promise对象的时候传递的参数是一个执行器函数,参数是resolve和reject,操作成功时传递resolve中的结果,操作失败时传递reject中的结果
使用then() 方法可以处理 Promise 对象成功的情况)Promise状态变为fulfilled时),接受一个回调函数作为参数,回调函数的参数就是异步操作的结果。使用 catch() 方法可以处理 Promise 对象失败的情况,接受一个回调函数作为参数,回调函数的参数是错误信息。
console.log('Start');
const promise = new Promise((resolve, reject) => {
console.log('Promise executor');
setTimeout(() => {
resolve('Done');
}, 1000);
});
promise.then(result => {
console.log(result);
});
console.log('End');
1. 主线程:Start
2. 主线程:构造函数new 创建实例时自动执行,Promise的构造和初始化是同步的
3. setTimeout 等主线程清空时再执行
4. promise.then()创造异步回调函数,等待Promise状态变为fulfilled后再执行 (异步微任务)
5. 主线程:End
6. 主线程清空,1s后执行setTimeout
7. ,执行resolve,将微任务队列的回调函数执行
输出:
Start
Promise executor
End
Done
Async & Await
async表示函数里有异步操作,await表示紧跟在后面的表达式需要等待结果。
Async
async 的作用就是让函数return一个new promise把async里面的内容包住(里面是同步的)。resolve、reject把值传递给then(), catch() 回调异步函数,只有async函数内部执行完所有同步操作/return/抛出错误后,才会改变Promise的状态执行对应的then/catch回调。
Promise 的解决时机
在 async 函数中,Promise 的解决发生在函数体内的同步代码执行完毕时。
这意味着,即使函数体内还有未完成的异步操作(如 setTimeout),Promise 也会被解决。
Await
await的意思是等待后面表达式的结果,所以意思就是立即执行await后面的表达式,然后.then{ await后面的语句}。
await命令后面是一个 Promise 对象,返回该对象的结果。如果不是 Promise 对象,就直接返回对应的值。await命令就是内部then命令,后面的内容是回调函数
任何一个await语句后面的 Promise 对象变为reject状态,那么整个async函数都会中断执行。
有时,我们希望即使前一个异步操作失败,也不要中断后面的异步操作。这时可以将第一个await放在try…catch结构里面,这样不管这个异步操作是否成功,第二个await都会执行。
await异步操作可以同步声明
// 写法一
let [foo, bar] = await Promise.all([getFoo(), getBar()]);
// 写法二
let fooPromise = getFoo();
let barPromise = getBar();
let foo = await fooPromise;
let bar = await barPromise;```
// Async
console.log('aaa');
(async ()=>{
console.log(111); //在async里面
})().then(()=>{
console.log(222); //在async的then里面
});
console.log('bbb');
输出:
aaa 111 bbb 222
// Await
console.log('aaa');
(async ()=>{
console.log(111);
await console.log(222);
console.log(333);
})().then(()=>{
console.log(444);
});
console.log('ddd');
输出:
aaa 111 222 ddd 333 444
console.log('aaa');
setTimeout(()=>console.log('t1'), 0);
(async ()=>{
console.log(111);
await console.log(222);
console.log(333);
setTimeout(()=>console.log('t2'), 0)
})().then(()=>{
console.log(444);
})
console.log('bbb');
输出:
aaa 111 222 bbb 333 444 t1 t2
虽然要执行setTimeout才能进入then,但是
setTimeout(function(){
console.log('1')
});
new Promise(function(resolve){
console.log('2')
for (var i = 0; i < 10000; i++){
i == 99 && resolve()
}}).then(function(){
console.log('3')}
)
console.log('4');
输出:
2 4 3 1
//执行顺序
async function async1() {
console.log('async1 start')
await async2();
console.log('async1 end')
}
async function async2() {
console.log('async2')
}
console.log('script start')
setTimeout(function () {
console.log('setTimeout')
}, 0)
async1();
new Promise(
function (resolve) {
console.log('promise1')
resolve();
})
.then(
function () {
console.log('promise2')
})
console.log('script end')
script start
async1 start
asncy2 .
promise1
script end
async1 end
promose2
setTimeout