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

228 天前
 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)
3256 次点击
所在节点    Node.js
26 条回复
lisongeee
228 天前
因为 Promise 的构造函数传入的函数参数是立刻执行
PaiDaTun
228 天前
```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)
```
onlxx123
228 天前
因为你的 Promise 函数里并没有微任务或类似 setTimeout 的事件循环任务,你把计算步骤放到 setTimeout(func, 0) 或者 queueMicrotask 里再试试。
wyc9296
228 天前
@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")
})
}
```
thinkershare
228 天前
JavaScript 在浏览器上,不使用 WebWorker 的情况下,并没有真正意义的并行执行,只不过是一堆后续操作和一堆任务及其逻辑依赖顺序罢了。
wyc9296
228 天前
问了一下微软的 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 的使用。它先执行了一个耗时的计算,然后在计算完成后才执行后续的操作。
如果你还有其他问题,欢迎继续提问!
lisongeee
228 天前
@wyc9296 #4

> Promise 不是异步调用的解决方案么?你的意思是说它是同步执行的?

1. 是, 但不是最终,最终方案是 async/await + Promise
2. 不完全是

Promise 的构造函数的函数参数是立刻执行,执行完后的 Promise.then 一般是微任务执行,特殊情况 iOS 小程序是宏任务

你别老整一堆回调试图用代码上的混乱打乱思维,你直接用 async await 线性流程写法就行
wyc9296
228 天前
@thinkershare 我是直接在主机运行的 nodejs 呀、没有在浏览器上。我理解的是,它是单线程的,所以所有任务都需要排队,我对它的排队顺序有点懵。
wyc9296
228 天前
@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)
```
lisongeee
228 天前
或者你直接看相关源码实现呗,你的计算操作在 https://github.com/zloirock/core-js/blob/66e55a9183394b88b5a71c2ded58719211f4421a/packages/core-js/modules/es.promise.constructor.js#L205 可以看到被直接执行
thinkershare
228 天前
@wyc9296 nodejs 的默认行为和浏览器上没有区别。你都理解什么是后续任务了,就应该明白,调度器只保证 A->B 这种 B 总是在 A 完成 resolve 后执行,其它都是不保证的。
clue
228 天前
认为 Promise 就是异步/微任务的, 都是理解不到家的

你这代码里, Promise 构造函数传入的初始化函数是同步执行的, 只有 then 传入的回调函数, 在 resolve("my func1") 执行完后被加到微任务队列中
Track13
228 天前
js 是单线程的。你这段代码没有需要等的地方。
DICK23
228 天前
Promise 构造函数是同步执行,后往 micro task queue 里加 then 中的函数,接着继续执行剩余的同步代码,等执行栈空了,再从队列中取出任务进到执行栈中,输出 my func1
onerd
228 天前
八股经典的手写 promise 看一下 myfunc1()这里 executor 立刻执行了
BeautifulSoap
228 天前
lz 对 js promises 和 js 的异步的理解有误。Promise 的异步本质是回调函数,以前 js 没有 Promise 的时候,js 和 Python 这些脚本一样,要实现异步只有用回调函数( callback function )来实现,结果就是导致回调地狱的出现。后来为了解决这个问题才有了 Promise ,但 Promise 本质上还是更好用的回调函数,并没有改变任何 js 的底层。然后 async await 是 Promise 的语法糖,async await 的代码看起来像是同步,实际上依旧是同步执行的回调

so ,js 中并不存在语言或者 js 引擎层面的任务排队这个概念,他就是一路同步执行下去的。然后 Peomis 构造函数是立刻执行,所以你在构造函数里做大量计算,这种会一直占用线程的操作的话,后面的操作自然不会去执行了
kenvix
228 天前
Promise 并不会切换线程或者把任务丢到一个工作线程池去执行,而是就在当前线程池运行。而你的任务,恰恰就是计算密集型任务,线程资源全在跑你的循环上。跑完循环才能继续流程
kenvix
228 天前
@kenvix #17 *口误,不是当前线程池,是当前线程
wpzz
228 天前
🎯 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
wpzz
228 天前
@wpzz 我这还有用 TypeScript 手写 Promise 的 demo

https://juejin.cn/post/7098763156209532959

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

https://www.v2ex.com/t/1030139

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

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

© 2021 V2EX