第 8 题:setTimeout、Promise、Async/Await 的区别

发布于 2022-06-21 12:54:49 字数 266 浏览 1220 评论 30

这题怎么没人答,我说下我粗浅的认识,抛砖引玉,欢迎指正和补充。
我觉得这题主要是考察这三者在事件循环中的区别,事件循环中分为宏任务队列和微任务队列。
其中settimeout的回调函数放到宏任务队列里,等到执行栈清空以后执行;
promise.then里的回调函数会放到相应宏任务的微任务队列里,等宏任务里面的同步代码执行完再执行;async函数表示函数里面可能会有异步方法,await后面跟一个表达式,async方法执行时,遇到await会立即执行表达式,然后把表达式后面的代码放到微任务队列里,让出执行栈让同步代码先执行。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(30

蓬勃野心 2022-05-04 13:57:20

2. 基础知识

需要先看懂这两份资料,他们会让你构建一个完整的从 上下文执行栈,Event Loop,任务队列(task queue),再到Microtask(微任务)、Macrotask/Task(宏任务)知识体系。看完这个来解决一些setTimeout,pormsie,async 的执行先后问题,简直都是毛毛雨!

js运行原理

首先补齐基础,来看一下js 引擎(如:V8)的运行原理,这位Philip Roberts小哥讲的非常好,运行过程都使用动画展现,过程非常生动,条理也很清楚,当然ppt也做的不错。
这是B站上带英文字幕的版本
视频地址

这个是核心思想的截图
evnet loop

Microtask、Macrotask/Task

Philip Roberts视频中缺少了任务队列(task queue)区分为Microtask(微任务)、Macrotask/Task(宏任务)的部分,这里需要看第二份资料,详细的介绍了Microtask、Macrotask/Task 的运行过程,且分析了浏览器的执行差异,Jake Archibald英文博客地址

看博客注意事项

  • 博客内有带执行步骤的动画,一定要亲自点一下

  • 博客中将Macrotask叫做Task;

  • 分析浏览器差异的部分基本可以略过了,随着版本更新,这些差异基本都被修补了。我们只要看博客中关于chrome浏览器的正确输出结果就可以了

Microtask和Macrotask所包含的api:

Microtask
- process.nextTick
- promise
- Object.observe (废弃)
- MutationObserver

Macrotask
- setTimeout
- setImmediate
- setInterval
- I/O
- UI 渲染

如果不想看英文博客,我在这里作一点简单的总结:

  1. 在执行上下文栈的同步任务执行完后;
  2. 首先执行Microtask队列,按照队列先进先出的原则,一次执行完所有Microtask队列任务;
  3. 然后执行Macrotask/Task队列,一次执行一个,一个执行完后,检测 Microtask是否为空;
  4. 为空则执行下一个Macrotask/Task;
  5. 不为空则执行Microtask

更多内容可以看我的github

笔芯 2022-05-04 13:57:20

宏观微观?我记得承诺的执行是在setimeout之前(好像),异步/ AWAIT是基于承诺的

宏观吧,我其实就是比较奇怪,等待前面的代码都跳出了线程吗,还是单纯就等待后面的执行跳出了线程,也可能是我表达有问题,为啥等待以及前面代码的都会先打印然后才是跳出线程执行外面的,最后才执行的await下面的代码。

就像setimeout是1,2,3这样的顺序来的,但是promise是1.1,1.2,promise是微观的,async,await基于promise也大概和promise一样
恩,这个其实是了解的,就是Async/Await的有点没吃透,Await前面的语句 和Await后面的语句,两者间的差异, 就是为啥不是await 的内容 先输出 然后await上面和下面再输出

几乎同样的疑问,,,我搞不懂为什么await下面的代码会在script end之后输出,,我以为是在async2执行完就会执行的 然后才执行 script end,,,,不太懂

歌入人心 2022-05-04 13:57:20

2. 基础知识

需要先看懂这两份资料,他们会让你构建一个完整的从 上下文执行栈,Event Loop,任务队列(task queue),再到Microtask(微任务)、Macrotask/Task(宏任务)知识体系。看完这个来解决一些setTimeout,pormsie,async 的执行先后问题,简直都是毛毛雨!

js运行原理

首先补齐基础,来看一下js 引擎(如:V8)的运行原理,这位Philip Roberts小哥讲的非常好,运行过程都使用动画展现,过程非常生动,条理也很清楚,当然ppt也做的不错。
这是B站上带英文字幕的版本
视频地址

这个是核心思想的截图
evnet loop

Microtask、Macrotask/Task

Philip Roberts视频中缺少了任务队列(task queue)区分为Microtask(微任务)、Macrotask/Task(宏任务)的部分,这里需要看第二份资料,详细的介绍了Microtask、Macrotask/Task 的运行过程,且分析了浏览器的执行差异,Jake Archibald英文博客地址

看博客注意事项

  • 博客内有带执行步骤的动画,一定要亲自点一下star
  • 博客中将Macrotask叫做Task;
  • 分析浏览器差异的部分基本可以略过了,随着版本更新,这些差异基本都被修补了。我们只要看博客中关于chrome浏览器的正确输出结果就可以了

Microtask和Macrotask所包含的api:

Microtask
- process.nextTick
- promise
- Object.observe (废弃)
- MutationObserver

Macrotask
- setTimeout
- setImmediate
- setInterval
- I/O
- UI 渲染

如果不想看英文博客,我在这里作一点简单的总结:

  1. 在执行上下文栈的同步任务执行完后;
  2. 首先执行Microtask队列,按照队列先进先出的原则,一次执行完所有Microtask队列任务;
  3. 然后执行Macrotask/Task队列,一次执行一个,一个执行完后,检测 Microtask是否为空;
  4. 为空则执行下一个Macrotask/Task;
  5. 不为空则执行Microtask

更多内容可以看我的github


你好,这里的例子是写错了吧,实际运行不是这样的结果呢

清泪尽 2022-05-04 13:57:20

文章中的例子确实写错了

2. 基础知识

需要先看懂这两份资料,他们会让你构建一个完整的从 上下文执行栈,Event Loop,任务队列(task queue),再到Microtask(微任务)、Macrotask/Task(宏任务)知识体系。看完这个来解决一些setTimeout,pormsie,async 的执行先后问题,简直都是毛毛雨!

js运行原理

首先补齐基础,来看一下js 引擎(如:V8)的运行原理,这位Philip Roberts小哥讲的非常好,运行过程都使用动画展现,过程非常生动,条理也很清楚,当然ppt也做的不错。
这是B站上带英文字幕的版本
视频地址
这个是核心思想的截图
evnet loop

Microtask、Macrotask/Task

Philip Roberts视频中缺少了任务队列(task queue)区分为Microtask(微任务)、Macrotask/Task(宏任务)的部分,这里需要看第二份资料,详细的介绍了Microtask、Macrotask/Task 的运行过程,且分析了浏览器的执行差异,Jake Archibald英文博客地址
看博客注意事项

  • 博客内有带执行步骤的动画,一定要亲自点一下star
  • 博客中将Macrotask叫做Task;
  • 分析浏览器差异的部分基本可以略过了,随着版本更新,这些差异基本都被修补了。我们只要看博客中关于chrome浏览器的正确输出结果就可以了

Microtask和Macrotask所包含的api:

Microtask
- process.nextTick
- promise
- Object.observe (废弃)
- MutationObserver

Macrotask
- setTimeout
- setImmediate
- setInterval
- I/O
- UI 渲染

如果不想看英文博客,我在这里作一点简单的总结:

  1. 在执行上下文栈的同步任务执行完后;
  2. 首先执行Microtask队列,按照队列先进先出的原则,一次执行完所有Microtask队列任务;
  3. 然后执行Macrotask/Task队列,一次执行一个,一个执行完后,检测 Microtask是否为空;
  4. 为空则执行下一个Macrotask/Task;
  5. 不为空则执行Microtask

更多内容可以看我的github


你好,这里的例子是写错了吧,实际运行不是这样的结果呢

文章中的的结果确实写错了,现在已经更正.谢谢你的指正! 如果对你造成误导,非常抱歉!

慕烟庭风 2022-05-04 13:57:20

这题怎么没人答,我说下我粗浅的认识,抛砖引玉,欢迎指正和补充。
我觉得这题主要是考察这三者在事件循环中的区别,事件循环中分为宏任务队列和微任务队列。
其中settimeout的回调函数放到宏任务队列里,等到执行栈清空以后执行;
promise.then里的回调函数会放到相应宏任务的微任务队列里,等宏任务里面的同步代码执行完再执行;async函数表示函数里面可能会有异步方法,await后面跟一个表达式,async方法执行时,遇到await会立即执行表达式,然后把表达式后面的代码放到微任务队列里,让出执行栈让同步代码先执行。

第一次执行栈的同步任务都完成后,接着处理的应该是微任务吧,然后再从宏任务队列里拿一条宏任务到执行栈中,等执行栈中的宏任务处理完,再去清空微任务队列。

凉宸 2022-05-04 13:57:20
               async function async1() {
			await async2();
			console.log('async1 end');
		}

		async function async2() {
			return 'async2';
			// return Promise.resolve('async2');
		}

		async1();

		new Promise(function(resolve) {
			resolve();
		}).then(function() {
			console.log('Promise then');
		});

async函数本身返回是一个promise,为什么async2显式返回promise和不显式返回,两行打印顺序不一致,什么原理?显式返回顺序:Promise then --> async1 end;返回普通值顺序:async1 end --> Promise then

陈独秀 2022-05-04 13:57:20

setTimeout 和promise 好理解, 宏任务队列和微任务队列。,
await fun() n balabala..
await返回一个promise 后执行 balabala 所以我的理解就像使用了.then(()=>balabala),也是进入的微任务队列。 这样执行的顺序也就明白了

土豪!!! 2022-05-04 13:57:20

有个问题,如果promise的finally方法里面出错了怎么办呀,怎么捕获呀?

finally也是返回一个promise,自然可以用catch捕获运行时错误。

爱人如己 2022-05-04 13:57:20
               async function async1() {
			await async2();
			console.log('async1 end');
		}

		async function async2() {
			return 'async2';
			// return Promise.resolve('async2');
		}

		async1();

		new Promise(function(resolve) {
			resolve();
		}).then(function() {
			console.log('Promise then');
		});

async函数本身返回是一个promise,为什么async2显式返回promise和不显式返回,两行打印顺序不一致,什么原理?显式返回顺序:Promise then --> async1 end;返回普通值顺序:async1 end --> Promise then

return ‘async2’时,执行代码相当于如下:
new Promise(function (resolve) {
resolve('async2');
}).then(function() {
console.log('async1 end');
});
console.log('async1 end');排在microtask queue前面,所以输出顺序是async1 end --> Promise then

return Promise.resolve('async2')时,执行代码相当于如下:
new Promise(function (resolve) {
resolve(Promise.resolve('async2'));
}).then(function() {
console.log('async1 end');
});
此时的then取决于里层promise的状态,console.log('async1 end')会排在microtask queue后面,所以输出顺序是Promise then --> async1 end

天煞孤星 2022-05-04 13:57:20

自己回答一波,若有错误请各位指出,互相学习!谢谢 ~
鉴于上面各位大佬回答的,其实这题主要还是考浏览器的EVENT LOOP
setTimeout属于宏任务,Promise属于微任务.而async和await其实是geneorator的语法糖,实质上最后返回的也是promise,所以我将其归为微任务(有错的,请指出,谢谢)。
而浏览器的执行顺序是在一开始会通篇扫描整个脚本,生成主执行栈,用于执行同步任务.而异步任务会加入至浏览器的任务队列中.当执行栈为空,就会去Task队列中(任务队列)取出需要执行的代码放入执行栈中去执行。而Task队列中,我们又再之前提及到分:微任务和宏任务
微任务的优先级大于宏任务,所以在执行栈为空的时候,首先会去执行Micortask(微任务)队列,执行完毕后再去取Macrotask(宏任务)队列去执行栈中执行,一次执行一个,再去检查Micortask(微任务),若存在则执行Microtask,若没有则取下一个Macrotask任务继续执行,直至为空。
以上,是我对EventLoop的回答,但是我还是想在此基础上回答下关于这三个的异步不同之处。

setTimeout

setTimeout的异步使用方法算是比较古老的回调函数方式,就是我们之前写Jquery的时候,ajax的最常见的使用方式,这种的好处在于用很简单的方式实现了异步的方式,从而解决了异步直肠子的问题(耗时任务,一直处于等待)。缺点:回调地狱,这是写了多年Jq的一直很恶心的地方,代码嵌套太多,牵一发而动全身。

Promise

优点:解决了回调函数的问题,可以使用链式结构,代码逻辑清晰。
缺点:无法取消,有时逻辑复杂then太多,then中嵌套then,错误捕获不到正确的位置,只能通过自己的catch或者写reject(erro)的回调来捕获。

async

至此,我们再次引出我们的Async/Await语法糖
优点:以同步代码的方式去写异步,代码逻辑清晰,符合我们平时写的逻辑。缺点:因为await将异步代码改造成了同步代码,如果多个异步代码没有依赖性却使用了await会导致性能上的降低。(这个是可以避免的)

辞别 2022-05-04 13:57:20

宏观微观?我记得承诺的执行是在setimeout之前(好像),异步/ AWAIT是基于承诺的

宏观吧,我其实就是比较奇怪,等待前面的代码都跳出了线程吗,还是单纯就等待后面的执行跳出了线程,也可能是我表达有问题,为啥等待以及前面代码的都会先打印然后才是跳出线程执行外面的,最后才执行的await下面的代码。

就像setimeout是1,2,3这样的顺序来的,但是promise是1.1,1.2,promise是微观的,async,await基于promise也大概和promise一样
恩,这个其实是了解的,就是Async/Await的有点没吃透,Await前面的语句 和Await后面的语句,两者间的差异, 就是为啥不是await 的内容 先输出 然后await上面和下面再输出

async/await不是同步了嘛,我也缕的不是很清楚,第10题那个回答挺好的

引用阮一峰老师书中一句话:“ async 函数返回一个 Promise 对象,当函数执行的时候,一旦遇到 await 就会先返回,等到触发的异步操作完成,再接着执行函数体内后面的语句。”
简单的说,先去执行后面的同步任务代码,执行完成后,也就是表达式中的 Promise 解析完成后继续执行 async 函数并返回解决结果。(其实还是本轮循环promise的问题,最后的resolve属于异步,位于本轮循环的末尾。)

async1 end --> Promise then

我试了 结果一样啊  都是 async1 end --> Promise then
离不开的别离 2022-05-04 13:57:20

@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
妞丶爷亲个 2022-05-04 13:57:20

1. setTimeout

console.log('script start')	//1. 打印 script start
setTimeout(function(){
    console.log('settimeout')	// 4. 打印 settimeout
})	// 2. 调用 setTimeout 函数,并定义其完成后执行的回调函数
console.log('script end')	//3. 打印 script start
// 输出顺序:script start->script end->settimeout

2. Promise

Promise本身是同步的立即执行函数, 当在executor中执行resolve或者reject的时候, 此时是异步操作, 会先执行then/catch等,当主栈完成后,才会去调用resolve/reject中存放的方法执行,打印p的时候,是打印的返回结果,一个Promise实例。

console.log('script start')
let promise1 = new Promise(function (resolve) {
    console.log('promise1')
    resolve()
    console.log('promise1 end')
}).then(function () {
    console.log('promise2')
})
setTimeout(function(){
    console.log('settimeout')
})
console.log('script end')
// 输出顺序: script start->promise1->promise1 end->script end->promise2->settimeout

当JS主线程执行到Promise对象时,

  • promise1.then() 的回调就是一个 task
  • promise1 是 resolved或rejected: 那这个 task 就会放入当前事件循环回合的 microtask queue
  • promise1 是 pending: 这个 task 就会放入 事件循环的未来的某个(可能下一个)回合的 microtask queue 中
  • setTimeout 的回调也是个 task ,它会被放入 macrotask queue 即使是 0ms 的情况

3. async/await

async function async1(){
   console.log('async1 start');
    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

async 函数返回一个 Promise 对象,当函数执行的时候,一旦遇到 await 就会先返回,等到触发的异步操作完成,再执行函数体内后面的语句。可以理解为,是让出了线程,跳出了 async 函数体。

举个例子:

async function func1() {
    return 1
}

console.log(func1())

在这里插入图片描述
很显然,func1的运行结果其实就是一个Promise对象。因此我们也可以使用then来处理后续逻辑。

func1().then(res => {
    console.log(res);  // 30
})

await的含义为等待,也就是 async 函数需要等待await后的函数执行完成并且有了返回结果(Promise对象)之后,才能继续执行下面的代码。await通过返回一个Promise对象来实现同步的效果。

更多可见setTimeout、Promise、Async/Await

针对最后两步的结果,为什么是script end->async1 end?async1到async2之后没有返回到async1?可以这么理解吗:async1中对于async1 end实际上相当于async2中await返回的Promise的then回调吗?所以被归类到异步任务队列中去了?而外部的script end是一个同步的任务,所以先打印出来了

七秒鱼° 2022-05-04 13:57:20

@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 呢.

灰色世界里的红玫瑰 2022-05-04 13:57:20

js异步执行流程

Event Loop

首先js执行由三部分组成,用于执行主线程的栈,叫做execution context stack,用于存放在对象数据的heap,一般使用内存,用于存放异步事件队列的event queue

  1. 先说一下主线程的栈的执行流程,每个执行函数会形成一个帧,若干个帧组成了调用栈,然后依据先进后出的执行方式,从最后一个帧执行到第一个帧,前面的帧拥有后面帧的上下文,这个就是层级上下文,每个函数执行完毕之后,就弹出这一帧,销毁当前上下文,当所有的帧执行完毕之后,调用栈就被清空了,主线程此时执行完毕,也就是所谓的主线程闲置状态
  2. 执行过程产的数据存放在heap中,相应的上下文销毁后,内部的数据对象就会被回收
  3. 同时,在主线程执行过程中会产生一些异步事件,这些事件会被推入一个叫做callback queue的队列中,先进先出,异步事件的执行取决于event loop,event loop会轮询event queue,每当主调用栈处于闲置状态,事件队列中一个事件就会被推入主调用栈,继续执行步骤1、2、3;以此循环实现event loop
  4. 大概说一下异步事件的来源和类型,
  • settimeout和setintel产生的事件回调,由定时器线程来管理定时任务,当一个定时任务被激活,一个事件就会被推入事件队列
  • ajax网络请求的事件回调,由专门的网络请求线程管理
  • IO操作,包括鼠标点击,键盘输入和手势操作,
  • DOM更改,如requestAnimationFrame事件,Image的onload,document的onload
    补充一下几乎 所有的异步事件都有专门的线程来管理,event loop只负责将队列中的事件推入调用栈,至于在什么时机推入,都是由专门的线程负责,定时器模块借助CPU时钟来定时推入事件,DOM的更改事件由渲染进程负责

宏任务与微任务

  • 微任务的调度会有一个单独的 queueMicroTask,微任务事件队列依赖于宏任务的执行,当主调用栈清空,event loop检查微微任务队列,然后全部执行完毕,才能执行下一个宏任务,在此期间如果产生新的微任务,就会推入一个新的微任务队列,大概可以理解为每个宏任务有一个属于自己的微任务队列,每当本身执行完毕,还要执行 在此期间产生的 附属的微任务队列
  • 最常见的微任务之一就是 promise 的then、finally、catch方法,当这些方法需要运行,就会产生一个微任务
遥远的绿洲 2022-05-04 13:57:20

宏观微观?我记得promise的执行是在setimeout之前(好像),async/await是基于promise的

因为其实setTimeout不是JavaScript的api,所以是在宏任务里,JavaScript的代码都是在微任务里执行的,所以执行之后才执行setTimeout的代码

り繁华旳梦境ヽ 2022-05-04 13:57:20

传给setTimeout的回调是异步的,会放在宏任务队列里;
调用Promise的构造函数是同步的,传给Promise.then、Promise.catch、Promise.finally的回调是异步的,会放在微任务队列里;
async函数体内的第一个await表达式是宏任务,下面的都算微任务。

↙温凉少女 2022-05-04 13:55:58

宏观微观?我记得承诺的执行是在setimeout之前(好像),异步/ AWAIT是基于承诺的

宏观吧,我其实就是比较奇怪,等待前面的代码都跳出了线程吗,还是单纯就等待后面的执行跳出了线程,也可能是我表达有问题,为啥等待以及前面代码的都会先打印然后才是跳出线程执行外面的,最后才执行的await下面的代码。

就像setimeout是1,2,3这样的顺序来的,但是promise是1.1,1.2,promise是微观的,async,await基于promise也大概和promise一样
恩,这个其实是了解的,就是Async/Await的有点没吃透,Await前面的语句 和Await后面的语句,两者间的差异, 就是为啥不是await 的内容 先输出 然后await上面和下面再输出

async/await不是同步了嘛,我也缕的不是很清楚,第10题那个回答挺好的

七七 2022-05-04 13:55:26

宏观微观?我记得承诺的执行是在setimeout之前(好像),异步/ AWAIT是基于承诺的

宏观吧,我其实就是比较奇怪,等待前面的代码都跳出了线程吗,还是单纯就等待后面的执行跳出了线程,也可能是我表达有问题,为啥等待以及前面代码的都会先打印然后才是跳出线程执行外面的,最后才执行的await下面的代码。

就像setimeout是1,2,3这样的顺序来的,但是promise是1.1,1.2,promise是微观的,async,await基于promise也大概和promise一样
恩,这个其实是了解的,就是Async/Await的有点没吃透,Await前面的语句 和Await后面的语句,两者间的差异, 就是为啥不是await 的内容 先输出 然后await上面和下面再输出

月下伊人醉 2022-05-04 13:55:17

宏观微观?我记得承诺的执行是在setimeout之前(好像),异步/ AWAIT是基于承诺的

宏观吧,我其实就是比较奇怪,等待前面的代码都跳出了线程吗,还是单纯就等待后面的执行跳出了线程,也可能是我表达有问题,为啥等待以及前面代码的都会先打印然后才是跳出线程执行外面的,最后才执行的await下面的代码。

就像setimeout是1,2,3这样的顺序来的,但是promise是1.1,1.2,promise是微观的,async,await基于promise也大概和promise一样

执笏见 2022-05-04 13:47:40

宏观微观?我记得promise的执行是在setimeout之前(好像),async/await是基于promise的

宏观吧,我其实就是比较奇怪,await前面的代码都跳出了线程吗,还是单纯就await 后面的执行跳出了线程, 也可能是我表达有问题, 为啥await以及前面代码的都会先打印 然后才是跳出线程执行外面的,最后才执行await下面的代码。

朱染▼ 2022-05-04 13:16:30

宏观微观?我记得promise的执行是在setimeout之前(好像),async/await是基于promise的

猫卆 2022-05-04 11:49:44
async1 start

写的很好,感谢大大的分享,我还有一个疑惑上面提到的Async/Await await执行的代码 ,会跳出线程,但是await上面的打印会出现在await执行打印的前面,意思是await前面的代码都会跳出线程(异步执行),只有await后面的代码才是同步执行这个意思吗?

漫漫岁月 2022-05-04 07:12:03

有个问题,如果promise的finally方法里面出错了怎么办呀,怎么捕获呀?

会返回一个 被reject 的promise对象。继续绑定catch函数可以捕获到

能怎样 2022-05-04 06:59:34

有个问题,如果promise的finally方法里面出错了怎么办呀,怎么捕获呀?

谁与争疯 2022-05-03 22:39:46

1. setTimeout

console.log('script start')	//1. 打印 script start
setTimeout(function(){
    console.log('settimeout')	// 4. 打印 settimeout
})	// 2. 调用 setTimeout 函数,并定义其完成后执行的回调函数
console.log('script end')	//3. 打印 script start
// 输出顺序:script start->script end->settimeout

2. Promise

Promise本身是同步的立即执行函数, 当在executor中执行resolve或者reject的时候, 此时是异步操作, 会先执行then/catch等,当主栈完成后,才会去调用resolve/reject中存放的方法执行,打印p的时候,是打印的返回结果,一个Promise实例。

console.log('script start')
let promise1 = new Promise(function (resolve) {
    console.log('promise1')
    resolve()
    console.log('promise1 end')
}).then(function () {
    console.log('promise2')
})
setTimeout(function(){
    console.log('settimeout')
})
console.log('script end')
// 输出顺序: script start->promise1->promise1 end->script end->promise2->settimeout

当JS主线程执行到Promise对象时,

  • promise1.then() 的回调就是一个 task
  • promise1 是 resolved或rejected: 那这个 task 就会放入当前事件循环回合的 microtask queue
  • promise1 是 pending: 这个 task 就会放入 事件循环的未来的某个(可能下一个)回合的 microtask queue 中
  • setTimeout 的回调也是个 task ,它会被放入 macrotask queue 即使是 0ms 的情况

3. async/await

async function async1(){
   console.log('async1 start');
    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

async 函数返回一个 Promise 对象,当函数执行的时候,一旦遇到 await 就会先返回,等到触发的异步操作完成,再执行函数体内后面的语句。可以理解为,是让出了线程,跳出了 async 函数体。

举个例子:

async function func1() {
    return 1
}

console.log(func1())

在这里插入图片描述
很显然,func1的运行结果其实就是一个Promise对象。因此我们也可以使用then来处理后续逻辑。

func1().then(res => {
    console.log(res);  // 30
})

await的含义为等待,也就是 async 函数需要等待await后的函数执行完成并且有了返回结果(Promise对象)之后,才能继续执行下面的代码。await通过返回一个Promise对象来实现同步的效果。

更多可见setTimeout、Promise、Async/Await
什么叫同步的立即执行函数?建议大佬看看规范中Promise的定义。

第几種人 2022-05-03 11:24:07

上面的解释都很详细了。

我拿 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 有在往那个方向走。

function* generator(i) {
  console.log('inside before')
  yield i;
  yield i + 10;
  console.log('inside after')
}

var gen = generator(10);
console.log('outside before')
console.log(gen.next().value);
console.log(gen.next().value);
console.log('outside after')
gen.next();

结果如下

> "outside before"
> "inside before"
> 10
> 20
> "outside after"
> "inside after" // 如果不加最后一个gen.next(); 就不会有这一行
无言温柔 2022-05-02 07:58:44

1. setTimeout

console.log('script start')	//1. 打印 script start
setTimeout(function(){
    console.log('settimeout')	// 4. 打印 settimeout
})	// 2. 调用 setTimeout 函数,并定义其完成后执行的回调函数
console.log('script end')	//3. 打印 script start
// 输出顺序:script start->script end->settimeout

2. Promise

Promise本身是同步的立即执行函数, 当在executor中执行resolve或者reject的时候, 此时是异步操作, 会先执行then/catch等,当主栈完成后,才会去调用resolve/reject中存放的方法执行,打印p的时候,是打印的返回结果,一个Promise实例。

console.log('script start')
let promise1 = new Promise(function (resolve) {
    console.log('promise1')
    resolve()
    console.log('promise1 end')
}).then(function () {
    console.log('promise2')
})
setTimeout(function(){
    console.log('settimeout')
})
console.log('script end')
// 输出顺序: script start->promise1->promise1 end->script end->promise2->settimeout

当JS主线程执行到Promise对象时,

  • promise1.then() 的回调就是一个 task

  • promise1 是 resolved或rejected: 那这个 task 就会放入当前事件循环回合的 microtask queue

  • promise1 是 pending: 这个 task 就会放入 事件循环的未来的某个(可能下一个)回合的 microtask queue 中

  • setTimeout 的回调也是个 task ,它会被放入 macrotask queue 即使是 0ms 的情况

3. async/await

async function async1(){
   console.log('async1 start');
    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

async 函数返回一个 Promise 对象,当函数执行的时候,一旦遇到 await 就会先返回,等到触发的异步操作完成,再执行函数体内后面的语句。可以理解为,是让出了线程,跳出了 async 函数体。

举个例子:

async function func1() {
    return 1
}

console.log(func1())

在这里插入图片描述
很显然,func1的运行结果其实就是一个Promise对象。因此我们也可以使用then来处理后续逻辑。

func1().then(res => {
    console.log(res);  // 30
})

await的含义为等待,也就是 async 函数需要等待await后的函数执行完成并且有了返回结果(Promise对象)之后,才能继续执行下面的代码。await通过返回一个Promise对象来实现同步的效果。

更多可见setTimeout、Promise、Async/Await

陌路终见情 2022-04-29 17:54:23

宏观任务队列
微观任务队列
的区别

~没有更多了~

关于作者

怪我鬧

暂无简介

0 文章
0 评论
24 人气
更多

推荐作者

亚希

文章 0 评论 0

cyp

文章 0 评论 0

北漠

文章 0 评论 0

11223456

文章 0 评论 0

坠似风落

文章 0 评论 0

游魂

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文