我理解的是:下面的代码会先打印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)
1
lisongeee 237 天前
因为 Promise 的构造函数传入的函数参数是立刻执行
|
2
PaiDaTun 237 天前
```javascript
async 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") }) } const r = await myfunc1() console.log(r) console.log(123456) ``` |
3
onlxx123 237 天前
因为你的 Promise 函数里并没有微任务或类似 setTimeout 的事件循环任务,你把计算步骤放到 setTimeout(func, 0) 或者 queueMicrotask 里再试试。
|
4
wyc9296 OP @lisongeee Promise 不是异步调用的解决方案么?你的意思是说它是同步执行的?所以可以说下面的代码,
myfunc1 和 myfunc2 是等价的吗? ```javascript 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") }) } function myfunc2(){ /*这一段是计算步骤开始*/ 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 } } /*这一段是计算步骤结束*/ return new Promise((resolve,rejects)=>{ resolve("my func2") }) } ``` |
5
thinkershare 237 天前 2
JavaScript 在浏览器上,不使用 WebWorker 的情况下,并没有真正意义的并行执行,只不过是一堆后续操作和一堆任务及其逻辑依赖顺序罢了。
|
6
wyc9296 OP 问了一下微软的 Copilot ,感觉还是不够智能:
@我:下面的代码会先打印 123456 ,然后执行 myfunc1 函数的计算步骤,最后等待执行完毕后打印 my func1 。实际是先执行 myfunc1 函数的计算步骤,执行完毕后打印 123456 ,再打印 my func1 。讲一下你的理解。 @Copilot: 这段代码的执行顺序如下: 首先,它会打印出 123456 。 然后,它会开始执行 myfunc1 函数的计算步骤。这个函数使用了 Promise 来处理异步操作。在这个函数中,它进行了一个非常大的循环计算,计算了 100000000 次,每次都将 i 乘以 index_j 。 当计算完成后,resolve("my func1") 会被调用,表示异步操作已经完成。 最后,它会打印出 my func1 。 实际上,由于 myfunc1 函数的计算步骤非常耗时,所以在执行完毕之前,它会先打印出 123456 ,然后再打印出 my func1 。 这段代码的目的是演示异步操作和 Promise 的使用。它先执行了一个耗时的计算,然后在计算完成后才执行后续的操作。 如果你还有其他问题,欢迎继续提问! |
7
lisongeee 237 天前
@wyc9296 #4
> Promise 不是异步调用的解决方案么?你的意思是说它是同步执行的? 1. 是, 但不是最终,最终方案是 async/await + Promise 2. 不完全是 Promise 的构造函数的函数参数是立刻执行,执行完后的 Promise.then 一般是微任务执行,特殊情况 iOS 小程序是宏任务 你别老整一堆回调试图用代码上的混乱打乱思维,你直接用 async await 线性流程写法就行 |
8
wyc9296 OP @thinkershare 我是直接在主机运行的 nodejs 呀、没有在浏览器上。我理解的是,它是单线程的,所以所有任务都需要排队,我对它的排队顺序有点懵。
|
9
wyc9296 OP @onlxx123 确实如你所说,谢谢你
```javascript function myfunc1_timeout(){ return new Promise((resolve,rejects)=>{ setTimeout(()=>{ /*这一段是计算步骤开始*/ 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") },0) }) } myfunc1_timeout() .then(result=>{ console.log(result) }) console.log(123456) ``` |
10
lisongeee 237 天前
|
11
thinkershare 237 天前
@wyc9296 nodejs 的默认行为和浏览器上没有区别。你都理解什么是后续任务了,就应该明白,调度器只保证 A->B 这种 B 总是在 A 完成 resolve 后执行,其它都是不保证的。
|
12
clue 237 天前 4
认为 Promise 就是异步/微任务的, 都是理解不到家的
你这代码里, Promise 构造函数传入的初始化函数是同步执行的, 只有 then 传入的回调函数, 在 resolve("my func1") 执行完后被加到微任务队列中 |
13
Track13 237 天前
js 是单线程的。你这段代码没有需要等的地方。
|
14
DICK23 237 天前
Promise 构造函数是同步执行,后往 micro task queue 里加 then 中的函数,接着继续执行剩余的同步代码,等执行栈空了,再从队列中取出任务进到执行栈中,输出 my func1
|
15
onerd 237 天前
八股经典的手写 promise 看一下 myfunc1()这里 executor 立刻执行了
|
16
BeautifulSoap 237 天前 via Android
lz 对 js promises 和 js 的异步的理解有误。Promise 的异步本质是回调函数,以前 js 没有 Promise 的时候,js 和 Python 这些脚本一样,要实现异步只有用回调函数( callback function )来实现,结果就是导致回调地狱的出现。后来为了解决这个问题才有了 Promise ,但 Promise 本质上还是更好用的回调函数,并没有改变任何 js 的底层。然后 async await 是 Promise 的语法糖,async await 的代码看起来像是同步,实际上依旧是同步执行的回调
so ,js 中并不存在语言或者 js 引擎层面的任务排队这个概念,他就是一路同步执行下去的。然后 Peomis 构造函数是立刻执行,所以你在构造函数里做大量计算,这种会一直占用线程的操作的话,后面的操作自然不会去执行了 |
17
kenvix 237 天前
Promise 并不会切换线程或者把任务丢到一个工作线程池去执行,而是就在当前线程池运行。而你的任务,恰恰就是计算密集型任务,线程资源全在跑你的循环上。跑完循环才能继续流程
|
19
wpzz 237 天前
🎯 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 本身是同步」这个概念 ``` // 这一部分代码是同步的 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 的会进入到异步 resolve("my func1") ``` https://juejin.cn/post/7096407782118653965 |
20
wpzz 237 天前
|
21
realJamespond 237 天前
promise 和 settimeout 一起用才是异步,不然就是同步,make sense ?
|
22
msg7086 237 天前
我来盲猜一下。
你以为 Promise 跑程序就像 Promise 跑 AJAX 一样是异步的,因为跑 AJAX 的时候,是先执行了后面的代码,再在 AJAX 收到回复以后异步执行 then()的内容。 但这个的异步是来自 AJAX (Asynchronous JavaScript and XML)里的 Asynchronous 而不是来自 Promise 。不管你用的是不是 Promise ,用 AJAX 就是异步的,不使用 AJAX 的代码默认就是同步的。 同理 setTimeout 也是一个异步功能,不管你用不用 Promise 他都是异步的。 |
23
msg7086 237 天前
换一种说法,不管是 Promise 也好 await 也好,要实现异步执行,必须要用异步执行功能。不管是 AJAX 也好,还是 setTimeout 也好,用了这些,你的程序才能异步起来,然后 Promise 也好 await 也好,才能用上异步执行功能。如果你的代码本来就是同步的,那你写 Promise 或者 await 都是白写,因为没有异步代码可以给你 await 。
|
24
JJvaryBig 237 天前
解释执行顺序如下:
调用 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 |
25
vialon17 236 天前
copy 了代码直接扔到 firefox 里面跑了下,
这个函数看来会直接阻塞主线程,并不是异步/微县城。 |
26
forty 218 天前
等价于以下代码, 这样看是不是更好理解了?
``` async function myfunc1(){ /*这一段是计算步骤开始*/ 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 } } /*这一段是计算步骤结束*/ return "my func1"; } myfunc1() // 同步执行 .then(result=>{ // 异步回调 console.log(result) }) console.log(123456) ``` |