Promise 和异步回调和我想的不一样

2024-04-07 10:06:25 +08:00
 wyc9296

我理解的是:下面的代码会先打印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)
3653 次点击
所在节点    Node.js
27 条回复
realJamespond
2024-04-07 15:27:31 +08:00
promise 和 settimeout 一起用才是异步,不然就是同步,make sense ?
msg7086
2024-04-07 15:36:19 +08:00
我来盲猜一下。
你以为 Promise 跑程序就像 Promise 跑 AJAX 一样是异步的,因为跑 AJAX 的时候,是先执行了后面的代码,再在 AJAX 收到回复以后异步执行 then()的内容。
但这个的异步是来自 AJAX (Asynchronous JavaScript and XML)里的 Asynchronous 而不是来自 Promise 。不管你用的是不是 Promise ,用 AJAX 就是异步的,不使用 AJAX 的代码默认就是同步的。
同理 setTimeout 也是一个异步功能,不管你用不用 Promise 他都是异步的。
msg7086
2024-04-07 15:40:51 +08:00
换一种说法,不管是 Promise 也好 await 也好,要实现异步执行,必须要用异步执行功能。不管是 AJAX 也好,还是 setTimeout 也好,用了这些,你的程序才能异步起来,然后 Promise 也好 await 也好,才能用上异步执行功能。如果你的代码本来就是同步的,那你写 Promise 或者 await 都是白写,因为没有异步代码可以给你 await 。
JJvaryBig
2024-04-07 15:47:29 +08:00
解释执行顺序如下:

调用 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
vialon17
2024-04-08 10:41:24 +08:00
copy 了代码直接扔到 firefox 里面跑了下,
这个函数看来会直接阻塞主线程,并不是异步/微县城。
forty
2024-04-26 11:43:16 +08:00
等价于以下代码, 这样看是不是更好理解了?

```
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)
```
gesse
213 天前
`for (let index = 1; index < 100000000; index++) {`

你这个直接把进程都卡住了,event loop 都无法执行了,建议在里面用 setTimout 模拟
```js
setTimeout(() => {
resolve("my func1")
}, 3000);
```

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://ex.noerr.eu.org/t/1030139

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX