-
Notifications
You must be signed in to change notification settings - Fork 3.3k
第 8 题:setTimeout、Promise、Async/Await 的区别 #33
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
宏观任务队列 |
上面的解释都很详细了。 我拿 babel es8 编译了下 async/await 结果是这样的 async function asyncTest() {
const ret = await asyncFunction();
} ---> function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {
try {
var info = gen[key](arg);
var value = info.value;
} catch (error) {
reject(error);
return;
}
if (info.done) {
resolve(value);
} else {
Promise.resolve(value).then(_next, _throw);
}
}
function _asyncToGenerator(fn) {
return function() {
var self = this,
args = arguments;
return new Promise(function(resolve, reject) {
var gen = fn.apply(self, args);
function _next(value) {
asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value);
}
function _throw(err) {
asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err);
}
_next(undefined);
});
};
}
function asyncTest() {
return _asyncTest.apply(this, arguments);
}
function _asyncTest() {
_asyncTest = _asyncToGenerator(function*() {
const ret = yield asyncFunction();
});
return _asyncTest.apply(this, arguments);
} await/async 是通过 Generator/function* 来实现的。 所以 async/await 的相关优势也来自于generator。 Generator 是一个可以暂停 function ,感觉推出 generator 的目的包括不仅限于 Callback Hell 和 Inversion of Control。 感觉整个 community 有在往那个方向走。
结果如下
|
|
有个问题,如果promise的finally方法里面出错了怎么办呀,怎么捕获呀? |
会返回一个 被reject 的promise对象。继续绑定catch函数可以捕获到 |
写的很好,感谢大大的分享,我还有一个疑惑上面提到的Async/Await await执行的代码 ,会跳出线程,但是await上面的打印会出现在await执行打印的前面,意思是await前面的代码都会跳出线程(异步执行),只有await后面的代码才是同步执行这个意思吗? |
宏观微观?我记得promise的执行是在setimeout之前(好像),async/await是基于promise的 |
宏观吧,我其实就是比较奇怪,await前面的代码都跳出了线程吗,还是单纯就await 后面的执行跳出了线程, 也可能是我表达有问题, 为啥await以及前面代码的都会先打印 然后才是跳出线程执行外面的,最后才执行await下面的代码。 |
就像setimeout是1,2,3这样的顺序来的,但是promise是1.1,1.2,promise是微观的,async,await基于promise也大概和promise一样 |
|
async/await不是同步了嘛,我也缕的不是很清楚,第10题那个回答挺好的 |
2. 基础知识需要先看懂这两份资料,他们会让你构建一个完整的从 上下文执行栈,Event Loop,任务队列(task queue),再到Microtask(微任务)、Macrotask/Task(宏任务)知识体系。看完这个来解决一些setTimeout,pormsie,async 的执行先后问题,简直都是毛毛雨! js运行原理首先补齐基础,来看一下js 引擎(如:V8)的运行原理,这位Philip Roberts小哥讲的非常好,运行过程都使用动画展现,过程非常生动,条理也很清楚,当然ppt也做的不错。 Microtask、Macrotask/TaskPhilip Roberts视频中缺少了任务队列(task queue)区分为Microtask(微任务)、Macrotask/Task(宏任务)的部分,这里需要看第二份资料,详细的介绍了Microtask、Macrotask/Task 的运行过程,且分析了浏览器的执行差异,Jake Archibald英文博客地址。 看博客注意事项
Microtask和Macrotask所包含的api:
如果不想看英文博客,我在这里作一点简单的总结:
|
几乎同样的疑问,,,我搞不懂为什么await下面的代码会在script end之后输出,,我以为是在async2执行完就会执行的 然后才执行 script end,,,,不太懂 |
|
文章中的例子确实写错了
文章中的的结果确实写错了,现在已经更正.谢谢你的指正! 如果对你造成误导,非常抱歉! |
第一次执行栈的同步任务都完成后,接着处理的应该是微任务吧,然后再从宏任务队列里拿一条宏任务到执行栈中,等执行栈中的宏任务处理完,再去清空微任务队列。 |
async函数本身返回是一个promise,为什么async2显式返回promise和不显式返回,两行打印顺序不一致,什么原理?显式返回顺序:Promise then --> async1 end;返回普通值顺序:async1 end --> Promise then |
setTimeout 和promise 好理解, 宏任务队列和微任务队列。, |
finally也是返回一个promise,自然可以用catch捕获运行时错误。 |
return ‘async2’时,执行代码相当于如下: return Promise.resolve('async2')时,执行代码相当于如下: |
自己回答一波,若有错误请各位指出,互相学习!谢谢 ~ setTimeoutsetTimeout的异步使用方法算是比较古老的回调函数方式,就是我们之前写Jquery的时候,ajax的最常见的使用方式,这种的好处在于用很简单的方式实现了异步的方式,从而解决了异步直肠子的问题(耗时任务,一直处于等待)。缺点:回调地狱,这是写了多年Jq的一直很恶心的地方,代码嵌套太多,牵一发而动全身。 Promise优点:解决了回调函数的问题,可以使用链式结构,代码逻辑清晰。 async至此,我们再次引出我们的Async/Await语法糖 |
|
@sisterAn 你好,关于你的 3.async/await 我觉得不是很完备。 async function async1(){
console.log('async1 start');
// 我理解是这里的 async2 函数是返回的 Premise 对象
// 但是 Premise 其本身就是一个宏任务 不用 await 也应该是同步执行的
await async2();
console.log('async1 end')
}
async function async2(){
console.log('async2')
}
console.log('script start');
async1();
console.log('script end')
// 输出顺序:script start->async1 start->async2->script end->async1 end 而且请看一下下面例子是否能够表明清楚 // 被添加入下一次宏任务队列
setTimeout(_ => console.log(6));
async function test() {
console.log(1);
// 我的理解是
// 遇到 await 先执行其同步内容
// 让出执行线程(跳出当前任务)同时推入微任务队列 继续往下执行
// 执行完之后 回来继续执行
await new Promise((resolve, reject) => {
console.log(2);
resolve();
}).then(_ => console.log(4));
// 这里 5 没有进入微任务队列 只不是相当于被挂起了
console.log(5);
}
test();
test();
console.log(3);
// 如果执行一次 test 函数 结果 => 123456
// 如果执行两次 test 函数 结果 => 1212344556 |
请教一下 执行两次test函数为什么不是 1212345456 呢. |
js异步执行流程Event Loop首先js执行由三部分组成,用于执行主线程的栈,叫做execution context stack,用于存放在对象数据的heap,一般使用内存,用于存放异步事件队列的event queue
宏任务与微任务
|
因为其实setTimeout不是JavaScript的api,所以是在宏任务里,JavaScript的代码都是在微任务里执行的,所以执行之后才执行setTimeout的代码 |
传给setTimeout的回调是异步的,会放在宏任务队列里; |
都是异步任务 setTimeout 属于宏任务 await 表达式会暂停整个 async 函数的执行进程并出让其控制权,只有当其等待的基于 promise 的异步操作被兑现或被拒绝之后才会恢复进程。promise 的解决值会被当作该 await 表达式的返回值 https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Statements/async_function async function async1(){
console.log('2');
await async2();
console.log('5')
}
async function async2(){
console.log('3')
}
console.log('1');
async1();
console.log('4')
// 1 2 3 4 5 |
setTimout这是运行环境代码,和js语言无关。所以chrome,Firefox,nodejs等运行环境对其实现可能不一样,但最后实现结果都一样,延迟执行一段代码。nodejs实现是通过timer,当然可以看c++源码,本质上很简单,就是死循环获取断点,实现event loop。 promisepromise就是callback函数的封装,通过js就可以自己实现一个。现在是通过c++等原生实现了一遍。相比回调函数,promise的逻辑更清晰,但最重要的是代码编写格式统一,可以实现race,all,等异步管理,rxjs就是非常好的工具。因为promise的统一格式,通过yield关键词,就可以封装出await和async,实现异步代码简化。 await和async首先es6增加了yield关键词和generator函数,实现了编程语言上的协程。具体实现可以看babel的转换代码,v8只是先进行语义分析,然后生成相应的generator函数,逻辑和babel一样。因为其中涉及到了具体代码,所以无法写出公共函数,总之是一种语言逻辑转换,通过死循环和switch返回结果来实现代码的挂起和执行。yield加promise的语法糖就是await和async。总之,异步代码最佳结果就是await和async,一般不需要yield代码,特别复杂使用rxjs。 |
??而浏览器的执行顺序是在一开始会通篇扫描整个脚本,生成主执行栈,用于执行同步任务.而异步任务会加入至浏览器的任务队列中.当执行栈为空,就会去Task队列中(任务队列)取出需要执行的代码放入执行栈中去执行。 :生成主执行栈,用于执行同步任务. 执行栈都为空了还执行个什么呢? |
这题怎么没人答,我说下我粗浅的认识,抛砖引玉,欢迎指正和补充。
我觉得这题主要是考察这三者在事件循环中的区别,事件循环中分为宏任务队列和微任务队列。
其中settimeout的回调函数放到宏任务队列里,等到执行栈清空以后执行;
promise.then里的回调函数会放到相应宏任务的微任务队列里,等宏任务里面的同步代码执行完再执行;async函数表示函数里面可能会有异步方法,await后面跟一个表达式,async方法执行时,遇到await会立即执行表达式,然后把表达式后面的代码放到微任务队列里,让出执行栈让同步代码先执行。
The text was updated successfully, but these errors were encountered: