Nodejs中Promise和异步回调和我想的不一样
我理解的是:下面的代码会先打印123456
,然后执行myfunc1
函数的计算步骤,最后等待执行完毕后打印my func1
。理论上主线程会先将myfunc1
加入到 Promise 队列中,然后直接执行console.log(123456)
,最后再逐步清空任务队列、即执行myfunc1
函数内 Promise 中的计算步骤。
而实际上是:先执行myfunc1
函数的计算步骤,执行完毕后打印123456
,最后再打印my func1
。
哪里没理解对呢?
function myfunc1(){ return new Promise((resolve,rejects)=>{ /*这一段是计算步骤开始*/ let i =1 for (let index = 1; index < 100000000; index++) { i = 1 for (let index_j = 1; index_j < 100; index_j++) { i *=index_j } } /*这一段是计算步骤结束*/ resolve("my func1") }) }
myfunc1() .then(result=>{ console.log(result) }) console.log(123456)
Nodejs中Promise和异步回调和我想的不一样
因为 Promise 的构造函数传入的函数参数是立刻执行
javascript<br>async function myfunc1() {<br> return new Promise((resolve, rejects) => {<br> /*这一段是计算步骤开始*/<br> let i = 1<br> for (let index = 1; index < 100000000; index++) {<br> i = 1<br> for (let index_j = 1; index_j < 100; index_j++) {<br> i *= index_j<br> }<br> }<br> /*这一段是计算步骤结束*/<br> resolve("my func1")<br> })<br> }<br> const r = await myfunc1()<br> console.log(r)<br> console.log(123456)<br>
因为你的 Promise 函数里并没有微任务或类似 setTimeout 的事件循环任务,你把计算步骤放到 setTimeout(func, 0) 或者 queueMicrotask 里再试试。
Promise 不是异步调用的解决方案么?你的意思是说它是同步执行的?所以可以说下面的代码,
myfunc1 和 myfunc2 是等价的吗?javascript<br>function myfunc1(){<br> return new Promise((resolve,rejects)=>{<br> /*这一段是计算步骤开始*/<br> let i =1<br> for (let index = 1; index < 100000000; index++) {<br> i = 1<br> for (let index_j = 1; index_j < 100; index_j++) {<br> i *=index_j<br> }<br> }<br> /*这一段是计算步骤结束*/<br> resolve("my func1")<br> })<br>}<br>function myfunc2(){<br> /*这一段是计算步骤开始*/<br> let i =1<br> for (let index = 1; index < 100000000; index++) {<br> i = 1<br> for (let index_j = 1; index_j < 100; index_j++) {<br> i *=index_j<br> }<br> }<br> /*这一段是计算步骤结束*/<br> return new Promise((resolve,rejects)=>{<br> resolve("my func2")<br> })<br>}<br>
JavaScript 在浏览器上,不使用 WebWorker 的情况下,并没有真正意义的并行执行,只不过是一堆后续操作和一堆任务及其逻辑依赖顺序罢了。
问了一下微软的 Copilot ,感觉还是不够智能:
我:下面的代码会先打印 123456 ,然后执行 myfunc1 函数的计算步骤,最后等待执行完毕后打印 my func1 。实际是先执行 myfunc1 函数的计算步骤,执行完毕后打印 123456 ,再打印 my func1 。讲一下你的理解。
:
这段代码的执行顺序如下:
首先,它会打印出 123456 。
然后,它会开始执行 myfunc1 函数的计算步骤。这个函数使用了 Promise 来处理异步操作。在这个函数中,它进行了一个非常大的循环计算,计算了 100000000 次,每次都将 i 乘以 index_j 。
当计算完成后,resolve(“my func1”) 会被调用,表示异步操作已经完成。
最后,它会打印出 my func1 。
实际上,由于 myfunc1 函数的计算步骤非常耗时,所以在执行完毕之前,它会先打印出 123456 ,然后再打印出 my func1 。
这段代码的目的是演示异步操作和 Promise 的使用。它先执行了一个耗时的计算,然后在计算完成后才执行后续的操作。
如果你还有其他问题,欢迎继续提问!
#4
> Promise 不是异步调用的解决方案么?你的意思是说它是同步执行的?
1. 是, 但不是最终,最终方案是 async/await + Promise
2. 不完全是
Promise 的构造函数的函数参数是立刻执行,执行完后的 Promise.then 一般是微任务执行,特殊情况 iOS 小程序是宏任务
你别老整一堆回调试图用代码上的混乱打乱思维,你直接用 async await 线性流程写法就行
我是直接在主机运行的 nodejs 呀、没有在浏览器上。我理解的是,它是单线程的,所以所有任务都需要排队,我对它的排队顺序有点懵。
确实如你所说,谢谢你javascript<br>function myfunc1_timeout(){<br> return new Promise((resolve,rejects)=>{<br> setTimeout(()=>{<br> /*这一段是计算步骤开始*/<br> let i =1<br> for (let index = 1; index < 100000000; index++) {<br> i = 1<br> for (let index_j = 1; index_j < 100; index_j++) {<br> i *=index_j<br> }<br> }<br> /*这一段是计算步骤结束*/<br> resolve("my func1")<br> },0)<br><br> })<br>}<br><br>myfunc1_timeout()<br>.then(result=>{<br> console.log(result)<br>})<br>console.log(123456)<br>
nodejs 的默认行为和浏览器上没有区别。你都理解什么是后续任务了,就应该明白,调度器只保证 A->B 这种 B 总是在 A 完成 resolve 后执行,其它都是不保证的。
认为 Promise 就是异步/微任务的, 都是理解不到家的
你这代码里, Promise 构造函数传入的初始化函数是同步执行的, 只有 then 传入的回调函数, 在 resolve(“my func1”) 执行完后被加到微任务队列中
js 是单线程的。你这段代码没有需要等的地方。
Promise 构造函数是同步执行,后往 micro task queue 里加 then 中的函数,接着继续执行剩余的同步代码,等执行栈空了,再从队列中取出任务进到执行栈中,输出 my func1
八股经典的手写 promise 看一下 myfunc1()这里 executor 立刻执行了
lz 对 js promises 和 js 的异步的理解有误。Promise 的异步本质是回调函数,以前 js 没有 Promise 的时候,js 和 Python 这些脚本一样,要实现异步只有用回调函数( callback function )来实现,结果就是导致回调地狱的出现。后来为了解决这个问题才有了 Promise ,但 Promise 本质上还是更好用的回调函数,并没有改变任何 js 的底层。然后 async await 是 Promise 的语法糖,async await 的代码看起来像是同步,实际上依旧是同步执行的回调
so ,js 中并不存在语言或者 js 引擎层面的任务排队这个概念,他就是一路同步执行下去的。然后 Peomis 构造函数是立刻执行,所以你在构造函数里做大量计算,这种会一直占用线程的操作的话,后面的操作自然不会去执行了
Promise 并不会切换线程或者把任务丢到一个工作线程池去执行,而是就在当前线程池运行。而你的任务,恰恰就是计算密集型任务,线程资源全在跑你的循环上。跑完循环才能继续流程
#17 *口误,不是当前线程池,是当前线程
🎯 new Promise 本身是同步, resolve,reject 是异步,await promise 会 阻塞下面代码的执行。
myfunc1()
.then(result=>{
console.log(result)
})// 这里会解析这个函数,变成 100000000 同步计算,计算需要时间,会卡住下面的同步打印
console.log(123456)
// 如果调换一下顺序就可以看出,立马打印后,计算一会再打印出 resolve
console.log(123456)
myfunc1()
.then(result=>{
console.log(result)
})
主要还是「 new Promise 本身是同步」这个概念<br>// 这一部分代码是同步的<br> let i =1<br> for (let index = 1; index < 100000000; index++) {<br> i = 1<br> for (let index_j = 1; index_j < 100; index_j++) {<br> i *=index_j<br> }<br> }<br><br>// resolve 的会进入到异步<br> resolve("my func1")<br><br>
https://juejin.cn/post/7096407782118653965
我这还有用 TypeScript 手写 Promise 的 demo
https://juejin.cn/post/7098763156209532959
promise 和 settimeout 一起用才是异步,不然就是同步,make sense ?
换一种说法,不管是 Promise 也好 await 也好,要实现异步执行,必须要用异步执行功能。不管是 AJAX 也好,还是 setTimeout 也好,用了这些,你的程序才能异步起来,然后 Promise 也好 await 也好,才能用上异步执行功能。如果你的代码本来就是同步的,那你写 Promise 或者 await 都是白写,因为没有异步代码可以给你 await 。
解释执行顺序如下:
调用 myfunc1():
函数 myfunc1() 被调用,它立即返回一个 Promise 对象。这个 Promise 对象会立即开始执行其内部的构造函数提供的异步任务。
执行 Promise 构造函数中的异步任务:
异步任务(即传给 Promise 构造函数的回调函数)开始执行。这里,该任务包含两个嵌套的循环,进行大量的计算工作。
计算过程中,控制台不会有任何输出,因为所有操作都在内存中进行,且由于计算量大,这部分可能耗时较长。
当计算步骤全部完成(即所有循环迭代结束),resolve(“my func1”) 被调用,将字符串 “my func1” 作为成功的结果传递给 Promise 链。
同步代码继续执行:
在 myfunc1() 返回 Promise 并开始执行异步任务后,JavaScript 引擎继续执行后面的同步代码。
下一行是 console.log(123456),因此立即输出数字 123456 到控制台。
微任务检查点:
当当前执行上下文(如事件循环的一个宏任务)中的所有同步代码执行完毕后,事件循环到达一个检查点,此时会处理待处理的微任务队列。
由于 myfunc1() 返回的 Promise 在之前已执行 resolve(),对应的 .then() 方法被注册为一个微任务,等待执行。
执行 .then() 中的回调:
微任务队列中的第一个任务就是之前注册的 .then() 回调,该回调函数接收 result 参数(即 “my func1”)并执行 console.log(result)。
控制台输出字符串 “my func1”,这是整个程序中的最后一个操作。
综上所述,代码的执行顺序是:
创建并开始执行 Promise ,其中包含大量计算。
同步输出 123456 到控制台。
计算完成后,Promise 解决,将 “my func1” 放入微任务队列。
事件循环处理微任务,执行 .then() 回调,输出 “my func1” 到控制台。
所以,最终在控制台看到的输出顺序是:
// 123456
// my func1
copy 了代码直接扔到 firefox 里面跑了下,
这个函数看来会直接阻塞主线程,并不是异步/微县城。
等价于以下代码, 这样看是不是更好理解了?<br>async function myfunc1(){<br> /*这一段是计算步骤开始*/<br> let i =1<br> for (let index = 1; index < 100000000; index++) {<br> i = 1<br> for (let index_j = 1; index_j < 100; index_j++) {<br> i *=index_j<br> }<br> }<br> /*这一段是计算步骤结束*/<br> return "my func1";<br>}<br><br>myfunc1() // 同步执行<br>.then(result=>{ // 异步回调<br> console.log(result)<br>})<br><br>console.log(123456)<br>
for (let index = 1; index < 100000000; index++) {
你这个直接把进程都卡住了,event loop 都无法执行了,建议在里面用 setTimout 模拟js<br> setTimeout(() => {<br> resolve("my func1")<br> }, 3000);<br>