异步是JS提供的一种机制。对于一些会花费时间来完成的异步表达式,JS执行器会在执行到此处的时候将本段代码挂起,去执行后面的代码。等到挂起的代码有了响应之后再对响应进行处理。
- 用于提升性能
- ‘Promise’将会显式或者隐式的被使用
- 关键字‘async’和‘await’也经常会被成对的使用在异步表达式中
- ‘await’语法糖必须用在async函数中
- ’await‘语法糖的功能是让执行器暂时‘关闭’异步功能。等当前的异步函数返回结果后再去执行后面的代码,而不是挂起当前并跳过
以一个例子来简单演示一下:
var resolveAfter2Seconds = function() {
console.log("starting slow promise");
return new Promise(resolve => {
setTimeout(function() {
resolve("slow");
console.log("slow promise is done");
}, 2000);
});
};
var resolveAfter1Second = function() {
console.log("starting fast promise");
return new Promise(resolve => {
setTimeout(function() {
resolve("fast");
console.log("fast promise is done");
}, 1000);
});
};
var sequentialStart = async function() {
console.log('==SEQUENTIAL START==');
// 1. Execution gets here almost instantly
const slow = await resolveAfter2Seconds();
console.log(slow); // 2. this runs 2 seconds after 1.
const fast = await resolveAfter1Second();
console.log(fast); // 3. this runs 3 seconds after 1.
}
var concurrentStart = async function() {
console.log('==CONCURRENT START with await==');
const slow = resolveAfter2Seconds(); // starts timer immediately
const fast = resolveAfter1Second(); // starts timer immediately
// 1. Execution gets here almost instantly
console.log(await slow); // 2. this runs 2 seconds after 1.
console.log(await fast); // 3. this runs 2 seconds after 1., immediately after 2., since fast is already resolved
}
var concurrentPromise = function() {
console.log('==CONCURRENT START with Promise.all==');
return Promise.all([resolveAfter2Seconds(), resolveAfter1Second()]).then((messages) => {
console.log(messages[0]); // slow
console.log(messages[1]); // fast
});
}
var parallel = async function() {
console.log('==PARALLEL with await Promise.all==');
// Start 2 "jobs" in parallel and wait for both of them to complete
await Promise.all([
(async()=>console.log(await resolveAfter2Seconds()))(),
(async()=>console.log(await resolveAfter1Second()))()
]);
}
// This function does not handle errors. See warning below!
var parallelPromise = function() {
console.log('==PARALLEL with Promise.then==');
resolveAfter2Seconds().then((message)=>console.log(message));
resolveAfter1Second().then((message)=>console.log(message));
}
sequentialStart(); // after 2 seconds, logs "slow", then after 1 more second, "fast"
// wait above to finish
setTimeout(concurrentStart, 4000); // after 2 seconds, logs "slow" and then "fast"
// wait again
setTimeout(concurrentPromise, 7000); // same as concurrentStart
// wait again
setTimeout(parallel, 10000); // truly parallel: after 1 second, logs "fast", then after 1 more second, "slow"
// wait again
setTimeout(parallelPromise, 13000); // same as parallel
首先,大概解释一下两个主要的被测异步函数’resolveAfter2Seconds‘和‘resolveAfter1Seconds’。这是两个典型的异步函数,会在执行之初打印出‘starting slow(fast) promise’,并分别在2秒和1秒后打印出‘slow(fast) promise is done’并返回promise被resolve的结果。
第一个函数‘sequentialStart’:当执行器执行到‘resolveAfter2Seconds‘时,将会打印’starting slow promise‘,此时后面的异步函数会开始被执行,但是由于这段语句使用了’await‘语法糖,所以执行器并不会往下执行,而是静止等待。2秒之后‘slow promise is done’被打印,变量slow的值‘slow’也被按顺序打印。之后执行器执行到了‘resolveAfter1Second‘的地方,跟前面类似,该异步函数也被按顺序执行,所以将会打印’starting fast promise‘,1秒之后‘fast promise is done’会被打印,最后,‘fast’作为变量fast也被打印出来
第二个函数‘concurrentStart’:当执行器执行到‘resolveAfter2Seconds‘时,将会打印’starting slow promise‘,然而后续的的执行是异步的并且需要时间。此时执行器挂起该函数并向下执行‘resolveAfter1Second’并打印‘starting fast promise’。此时的状态是,两个‘setTimeout’同时执行。同时,执行器也不会像后面执行是因为后面的‘log’语句带有‘await’语法糖。由于‘resolveAfter1Second’比‘resolveAfter2Second’更快结束,所以‘fast promise is done’先被打印出来,然而,由于‘console.log’不是一个异步函数,所以执行器会在’await slow‘处等待。之后,‘resolveAfter2Second’结束执行,打印出了’slow promise is done‘以及‘slow’。最后,打印‘fast’的语句被执行
第三个函数‘concurrentPromise’:这里‘Promise.all’的意思是,当数组内所有的‘Promise’都已完成并且状态是‘resolved’时,执行后续的‘then’里面的表达式。当执行器执行到此处时,首先‘resolveAfter2Seconds’会被执行,此时会打印‘starting slow promise’,同时‘resolveAfter1Second’也会被执行,此时会打印‘starting fast promise’。之后‘resolveAfter1Second’率先完成,打印了‘fast promise is done’。最后‘resolveAfter2Seconds’完成,打印了‘slow promise is done’。在所有异步完成之后,‘then’里面的语句被执行,此时‘slow’和‘fast’被分别打印出来
第四个函数‘parallel’:此时‘Promise.all’数组里面的两个‘async’语句同时被执行。但是因为‘await’的存在,‘log’会等到后面的两个‘resolve’函数取得结果的时候才会执行。‘resolveAfter2Seconds’和‘resolveAfter1Seconds’会同时执行,此时会分别打印出‘starting slow promise’和‘starting fast promise’。之后,‘resolveAfter1Seconds’将会首先结束,‘fast promise is done’会被打印。之后由于’resolveAfter1Second‘执行结束,它的结果’fast‘也将由’log‘打印出来。最后,‘resolveAfter2Seconds’执行结束,‘slow promise is done’和‘slow’会被打印
第五个函数‘parallelPromise’:当执行器执行到’resolveAfter2Seconds‘语句的时候,会打印出‘starting slow promise’。之后由于该函数是个异步函数,所以执行器会跳过当前的等待转而执行后面的语句:‘starting fast promise’会被打印。之后由于‘resolveAfter1Seconds’首先完成,所以‘fast promise is done’会被打印,此时由于promise也被完成,所以对应的‘then’语句被执行:‘fast’被打印出来。之后‘resolveAfter2Seconds’执行完成,’slow promise is done‘和‘slow’将会被打印出来
下附demo代码的执行结果:
- "==SEQUENTIAL START=="
- "starting slow promise"
- "slow promise is done"
- "slow"
- "starting fast promise"
- "fast promise is done"
- "fast"
- "==CONCURRENT START with await=="
- "starting slow promise"
- "starting fast promise"
- "fast promise is done"
- "slow promise is done"
- "slow"
- "fast"
- "==CONCURRENT START with Promise.all=="
- "starting slow promise"
- "starting fast promise"
- "fast promise is done"
- "slow promise is done"
- "slow"
- "fast"
- "==PARALLEL with await Promise.all=="
- "starting slow promise"
- "starting fast promise"
- "fast promise is done"
- "fast"
- "slow promise is done"
- "slow"
- "==PARALLEL with Promise.then=="
- "starting slow promise"
- "starting fast promise"
- "fast promise is done"
- "fast"
- "slow promise is done"
- "slow"
JS并发模型解释
上图是JS并发的模型,首先有几个重要概念需要解释: 栈:里面存放有‘帧’。帧是一个个的JS任务。 堆:里面存放的是当前JS声明好的对象、变量等等 队列:里面存放有‘消息’
它的工作过程是这样的:假设初始状态队列、栈、堆内都有相应的内容。此时JS会优先按顺序处理‘栈’内的‘帧’任务。等到‘栈’内为空时去读取底下消息队列中‘消息’。该消息为异步任务的响应,此时JS会将异步任务对应的回调函数作为‘帧’压入‘栈’中进行处理。直到‘栈’被清空时读取下一个消息。
JS异步过程解释
JS是单线程机制,它拥有一个主线程以及若干个支线程。在JS执行过程中,主线程负责执行同步语句,当遇到需要等待的异步语句时,它会另开一个线程专门执行异步,同时主线程继续进行所在的同步任务。当主线程执行完毕并且支线程的异步语句执行完毕后,对应的异步回调函数会被在主线程中执行。对比上图,栈就是主线程的实现,队列就是支线程的实现
参考: developer.mozilla.org/zh-CN/docs/… www.freecodecamp.org/news/synchr… juejin.cn/post/684490…