Skip to content

第 13 题:Promise 构造函数是同步执行还是异步执行,那么 then 方法呢? #19

@liiiku

Description

@liiiku
const promise = new Promise((resolve, reject) => {
  console.log(1)
  resolve()
  console.log(2)
})

promise.then(() => {
  console.log(3)
})

console.log(4)

执行结果是:1243
promise构造函数是同步执行的,then方法是异步执行的

Activity

kunish

kunish commented on Mar 7, 2019

@kunish

这里对上面的例子做一个扩展

const promise = new Promise((resolve, reject) => {
  console.log(1);
  resolve(5);
  console.log(2);
}).then(val => {
  console.log(val);
});

promise.then(() => {
  console.log(3);
});

console.log(4);

setTimeout(function() {
  console.log(6);
});

执行结果: 124536

LwDsmile

LwDsmile commented on Mar 12, 2019

@LwDsmile

学习了

KaiOrange

KaiOrange commented on Mar 14, 2019

@KaiOrange

Promise new的时候会立即执行里面的代码 then是微任务 会在本次任务执行完的时候执行 setTimeout是宏任务 会在下次任务执行的时候执行

pioneer26

pioneer26 commented on Mar 27, 2019

@pioneer26

excutor执行器里面是同步执行的,then里面是异步操作

yu910709

yu910709 commented on Apr 2, 2019

@yu910709

这里对上面的例子做一个扩展

const promise = new Promise((resolve, reject) => {
  console.log(1);
  resolve(5);
  console.log(2);
}).then(val => {
  console.log(val);
});

promise.then(() => {
  console.log(3);
});

console.log(4);

setTimeout(function() {
  console.log(6);
});

执行结果: 124536

请问为什么3在val之前?

cliYao

cliYao commented on Apr 17, 2019

@cliYao

这里对上面的例子做一个扩展

const promise = new Promise((resolve, reject) => {
  console.log(1);
  resolve(5);
  console.log(2);
}).then(val => {
  console.log(val);
});

promise.then(() => {
  console.log(3);
});

console.log(4);

setTimeout(function() {
  console.log(6);
});

执行结果: 124536

请问为什么3在val之前?

3是在5后面打印出来的啊,第一轮事件循环的时候,microtask queue里面先添加的promise.resolve(5).then((val)=>{console.log(val)}),后添加的promise.then(() => {
console.log(3);
});

changed the title [-]关于第13题的见解[/-] [+]第 13 题:Promise 构造函数是同步执行还是异步执行,那么 then 方法呢?[/+] on Apr 26, 2019
sunas

sunas commented on Jul 9, 2019

@sunas

Promise必然是同步的。就then我补充一下:
在ES6时代有了微异步的设定,then作为最典型代表,算是异步的一员。
在ES5时代,实现then的方式则要看构造函数里resolve(或reject)的用法了,如果resolve被同步使用,实质上resolve仍然是同步的。

wuzhong1030

wuzhong1030 commented on Jul 10, 2019

@wuzhong1030

Promise 构造函数是同步执行,then 是异步执行。看一下 Promise 的实现就知道了。

zhukunpenglinyutong

zhukunpenglinyutong commented on Jul 11, 2019

@zhukunpenglinyutong

看过 Event Loop 基础原理的就明白,Promise构造函数是同步执行,而 .then .catch .啥啥的是异步(还有process.nextTick等等,大家可以查),
而且放到了微队列中,async/await 中,await 前面的是同步,await 后面的是异步,写法上是这样,但是其实是 语法糖,最后还会转为 Promise.then的形式

Hunterang

Hunterang commented on Jul 11, 2019

@Hunterang

.then()当然是同步执行,只不过是.then的cb被放入了微任务队列,产生了异步执行

iamwelk

iamwelk commented on Jul 13, 2019

@iamwelk

这个题和之前的第8题类似

june38liu

june38liu commented on Jul 22, 2019

@june38liu

then()到底是同步执行还是异步执行?为什么回答里有的说是同步有的说是异步

thinkfish

thinkfish commented on Jul 22, 2019

@thinkfish

then()到底是同步执行还是异步执行?为什么回答里有的说是同步有的说是异步

根据上面的回答,总结下应该是then函数本身是同步,then里面的cb是异步

17 remaining items

fengmiaosen

fengmiaosen commented on Jul 6, 2020

@fengmiaosen

.then()当然是同步执行,只不过是.then的cb被放入了微任务队列,产生了异步执行

更详细一点的说法是等到 promise变为 resolved状态的时候,then注册的回调函数才被放入到微任务队列中,等待调用执行

slogeor

slogeor commented on Jul 12, 2020

@slogeor

.then()当然是同步执行,只不过是.then的cb被放入了微任务队列,产生了异步执行

更详细一点的说法是等到 promise变为 resolved状态的时候,then注册的回调函数才被放入到微任务队列中,等待调用执行

promise = new Promise((resolve, reject) => {
    console.log(1);
    setTimeout(() => {
        resolve(5);
    }, 10)
    console.log(2);
  }).then(val => {
    console.log(val);
  });
  
  promise.then(() => {
    console.log(3);
  });
  
  console.log(4);
  
  setTimeout(function() {
    console.log(6);
  });

补段代码

tjwyz

tjwyz commented on Jul 16, 2020

@tjwyz

做戏做全套... 写一个更有助于理解

ps: 微任务以宏任务(setTimeout)替代

class PromiseA {
    constructor(fn) {
        this.status = 'pending';
        this.value = null;
        this.error = null;

        this.resolveArr = [];
        this.rejectArr = [];

        let resolve = (value) => {
            // 防止多次resolve
            if (this.status !== 'pending') return;
            setTimeout(()=>{
                this.value = value;
                this.status = 'fullfiled';
                // 派发过往订阅
                while(this.resolveArr.length) {
                    let tmp = this.resolveArr.shift();
                    tmp(value);
                }
            }, 0);
        }
        let reject = (err) => {
            // 防止多次reject
            if (this.status !== 'pending') return;
            setTimeout(()=>{
                this.status = 'rejected';
                this.error = err;
                // 派发过往订阅
                while(this.rejectArr.length) {
                    let tmp = this.rejectArr.shift();
                    tmp(err);
                }
            }, 0);
        }
        fn(resolve, reject);
    }
    then(fun1, fun2) {
        // 根据规范,如果then的参数不是function,则我们需要忽略它, 让链式调用继续往下执行
        (typeof fun1 !== 'function') && (fun1 = value => value)
        // 
        (typeof fun2 !== 'function') && (fun2 = error => {
            throw(error);
        })

        return new PromiseA((resolve, reject)=>{
            let resolveItem = (param) => {
                try {
                    let tmp = fun1(param);
                    tmp instanceof PromiseA ? tmp.then(resolve, reject) : resolve(tmp);
                } catch (e) {
                    reject(e);
                }
            }
            let rejectItem = (err) => {
                try {
                    let tmp = fun2(err);
                    tmp instanceof PromiseA ? tmp.then(resolve, reject) : resolve(tmp);
                } catch (e) {
                    reject(e);
                }
            }

            if (this.status == 'pending') {
                this.resolveArr.push(resolveItem);
                this.rejectArr.push(rejectItem);
            } else if (this.status == 'fullfiled') {
                resolveItem(this.value);
            } else if (this.status == 'rejected') {
                rejectItem(this.error);
            }
        });
    }
    catch(rejectFn) {
        return this.then(null, rejectFn);
    }
    finally(fn) {
        // 保证了fn执行在前.. 但是有点绕
        return this.then((param)=>{
            // 万一 fn reject了
            return PromiseA.resolve(fn()).then(()=>param, ()=>param);
        }, (err) =>{
            // 万一 fn reject了
            return PromiseA.resolve(fn()).then(()=>{
                throw err;
            }, ()=>{
                throw err;
            });
        })
    }
    static resolve(param) {
        if (param instanceof PromiseA) return param;
        return new PromiseA((resolve)=>{
            resolve(param);
        })
    }
    static reject(param) {
        if (param instanceof PromiseA) return param;
        return new PromiseA((resolve, reject)=>{
            reject(param);
        })
    }

    static all(arr) {
        return new PromiseA((resolve, reject)=>{
            let i = 0;
            let ret = [];
            while(arr.length) {
                let tmp = arr.shift();
                tmp.then((param)=>{
                    ret[i] = param;
                    i++
                    if (i == arr.length) {
                        resolve(ret);
                    }
                },(err) => {
                    reject(err);
                })
            }
        });
    }
    static race(arr) {
        // 只要p1、p2、p3之中有一个实例率先改变状态,p的状态就跟着改变。
        return new PromiseA((resolve, reject) => {
            arr.forEach((item)=>{
                // 状态变化是单向的
                item.then(resolve, reject);
            })
        })
    }
    // 允许reject X次 失败后Y秒继续触发
    static retry(fn, x, y) {
        return PromiseA((resolve, reject)=>{
            let failTimes = 0;
            // 也可以把历代err保存起来
            // let errArr = [];

            function cb () {
                fn().then(resolve).catch((err)=>{
                    if (failTimes == x) {
                        reject(err);
                        return;
                    }

                    setTimeout(()=> {
                        cb();
                    }, y);

                    failTimes++;
                });
            }
            cb();
        }) 
    }

    static promisefy (cb) {
        return (...arg)=>{
            return new Promise((reoslve, reject)=>{
                cb(...arg, reoslve);
            })
        }
    }
}
MrLeihe

MrLeihe commented on Apr 14, 2021

@MrLeihe

promise 的构造函数是同步执行的,then方法也是同步执行的,只不过 then 里面的回调函数是异步执行的。

TenviLi

TenviLi commented on Apr 26, 2021

@TenviLi

⚠ 同志们,这道题绝对没有你们想象的这么简单,补充一个不错的文章中的摘录:

在Promises/A+规范的Notes 3.1中提及了promise的then方法可以采用“宏任务(macro-task)”机制或者“微任务(micro-task)”机制来实现。所以开头提及的promise在不同浏览器的差异正源于此,有的浏览器将then放入了macro-task队列,有的放入了micro-task 队列。在jake的博文Tasks, microtasks, queues and schedules中提及了一个讨论vague mailing list discussions,一个普遍的共识是promises属于microtasks队列。

—— https://segmentfault.com/a/1190000038985579

leeFengHuo

leeFengHuo commented on May 26, 2021

@leeFengHuo

const promise = new Promise(async (resolve, reject) => {
console.log(1)
await resolve()
console.log(2)
})

promise.then(() => {
console.log(3)
})

console.log(4);
碰到过这种扩展,想了解为什么2在3之前执行

Chorer

Chorer commented on Aug 6, 2021

@Chorer

const promise = new Promise(async (resolve, reject) => {
console.log(1)
await resolve()
console.log(2)
})

promise.then(() => {
console.log(3)
})

console.log(4);
碰到过这种扩展,想了解为什么2在3之前执行

这种出现 await 的代码,你可以等价转化为:

const promise = new Promise(async (resolve, reject) => {
   console.log(1)
   Promise.resolve(resolve()).then(() => {
       console.log(2)
   })
})
promise.then(() => {
   console.log(3)
})

console.log(4);

也就是将 await 后面跟着的部分用一个 Promise.resolve() 包裹起来,然后剩余部分塞到一个 then 的回调函数里。这样再分析就简单了。

gromy92

gromy92 commented on Mar 4, 2022

@gromy92

严格来说then方法 是同步执行的,then方法的参数(回调函数)是 异步执行的

xuchao996

xuchao996 commented on Mar 8, 2022

@xuchao996

excutor执行器里面是同步执行的,then里面是异步操作

如何体现呢?能举例说明吗?

yumengbdw

yumengbdw commented on Mar 17, 2022

@yumengbdw

之所以then是异步是为了处理循环引用的问题。

const p1 = promise1.then(value => {
    return p1
})

判断then里面new的promise 跟successCallback执行后得到的p1是不是同一个promise。
但是then里面处理回调的时候new的promise还没有创建完成。

Yuweiai

Yuweiai commented on Jul 4, 2022

@Yuweiai
const promise = new Promise((resolve, reject) => {
  console.log(1)
  resolve()
  console.log(2)
})

promise.then(() => {
  console.log(3)
})

console.log(4)

执行结果是:1243 promise构造函数是同步执行的,then方法是异步执行的

【resolve对比await】这里虽然有resolve,但会打印同时先后打印1/2,如果是await,会先执行await后面的表达式,然后将其后的代码加入到微任务队列

WangYunFei1

WangYunFei1 commented on Jul 13, 2022

@WangYunFei1

promise是同步执行的,then也是同步的,只是then中的回调时异步执行的

Yangfan2016

Yangfan2016 commented on Aug 23, 2022

@Yangfan2016

可以看 promise的 实现

resolve 回调里的内容 会放入 微任务中
then 的回调只是一个list push 动作,
等 resolve 里的 微任务 执行完,就会 调用 then 里的 回调

so 构造器里除了 resolve 之外的 都是 同步执行的(await resolve() 这种情况除外)
then 里的回调是异步执行的

beinian717

beinian717 commented on Oct 8, 2024

@beinian717

image

请问有完整代码吗?

cliYao

cliYao commented on Oct 8, 2024

@cliYao
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

      Development

      No branches or pull requests

        Participants

        @thinkfish@fengmiaosen@slogeor@sunas@iamwelk

        Issue actions

          第 13 题:Promise 构造函数是同步执行还是异步执行,那么 then 方法呢? · Issue #19 · Advanced-Frontend/Daily-Interview-Question