第 13 题:Promise 构造函数是同步执行还是异步执行,那么 then 方法呢?
liiiku opened this issue · 41 comments
const promise = new Promise((resolve, reject) => {
console.log(1)
resolve()
console.log(2)
})
promise.then(() => {
console.log(3)
})
console.log(4)
执行结果是:1243
promise构造函数是同步执行的,then方法是异步执行的
这里对上面的例子做一个扩展
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
学习了
Promise new的时候会立即执行里面的代码 then是微任务 会在本次任务执行完的时候执行 setTimeout是宏任务 会在下次任务执行的时候执行
excutor执行器里面是同步执行的,then里面是异步操作
这里对上面的例子做一个扩展
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之前?
这里对上面的例子做一个扩展
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);
});
Promise必然是同步的。就then我补充一下:
在ES6时代有了微异步的设定,then作为最典型代表,算是异步的一员。
在ES5时代,实现then的方式则要看构造函数里resolve(或reject)的用法了,如果resolve被同步使用,实质上resolve仍然是同步的。
Promise 构造函数是同步执行,then 是异步执行。看一下 Promise 的实现就知道了。
看过 Event Loop 基础原理的就明白,Promise构造函数是同步执行,而 .then .catch .啥啥的是异步(还有process.nextTick等等,大家可以查),
而且放到了微队列中,async/await 中,await 前面的是同步,await 后面的是异步,写法上是这样,但是其实是 语法糖,最后还会转为 Promise.then的形式
.then()当然是同步执行,只不过是.then的cb被放入了微任务队列,产生了异步执行
这个题和之前的第8题类似
then()到底是同步执行还是异步执行?为什么回答里有的说是同步有的说是异步
then()到底是同步执行还是异步执行?为什么回答里有的说是同步有的说是异步
根据上面的回答,总结下应该是then函数本身是同步,then里面的cb是异步
只要好好理解下 Promise, 很多疑问会迎刃而解
这里对上面的例子做一个扩展
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和6之间会有一个“随机数字”,它是怎么出现的,代表的什么呢?
这里对上面的例子做一个扩展
常量 承诺 = 新 无极((决心,拒绝)=> { 控制台。日志(1); 解析(5); 控制台。日志(2); })。然后(VAL => { 控制台。日志(VAL); }); 承诺。然后(()=> { 控制台。日志(3); }); 控制台。log(4); 的setTimeout(函数(){ 控制台。日志(6); });执行结果:124536
请问在浏览器控制器中执行的时候,打印的3和6之间会有一个“随机数字”,它是怎么出现的,代表的什么呢?
随机数字是seTimeout
方法的返回值
setTimeout()
方法的返回值是一个 **唯一的数值 **
如果你想要终止setTimeout()
方法的执行,那就必须使用 clearTimeout()
方法来终止,而使用这个方法的时候,系统必须知道你到底要终止的是哪一个setTimeout()
方法(因为你可能同时调用了好几个 setTimeout()
方法),这样clearTimeout()
方法就需要一个参数,这个参数就是setTimeout()
方法的返回值(数值),
用这个数值来唯一确定结束哪一个setTimeout()
方法。
这里对上面的例子做一个扩展
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
resolve(5)也能调用到下面这个then方法吗
promise.then(() => {
console.log(3);
});
promise是微观任务,setTimeout是宏观任务,先执行微观任务,在执行宏观任务;微观任务里,先执行同步再执行异步 所以结果是 124536
promise构造函数肯定是同步执行的,new Promise和其他的什么new Map、new Set一样的都是同步执行,当执行resolve方法将promise状态更新为fulfilled,并返回一个promise对象,调用then就会将then中的回调放入微任务队列中,也就是说promise.then才是异步
严格来说 构造函数 then都是同步的, 只是then内部的这个参数回调函数需要看resolve执行时机 并且内部做了个定时器, 所以回调才是异步的
整体代码、setTimeout、setInterval、Promise构造函数是宏任务,promise的then方法是微任务
假如有这么一段代码
new Promise(resolve => {
resolve(1);
Promise.resolve().then(() => console.log(2));
Promise.resolve().then(() => console.log(5));
}).then(t => console.log(t));
请问为什么输出结果是2,5,1呢?
resolve(1)
之后,不应该是优先注册了外部 .then
的回调吗?
还是说内部的 Promise
是同步执行的?
应该
假如有这么一段代码
new Promise(resolve => { resolve(1); Promise.resolve().then(() => console.log(2)); Promise.resolve().then(() => console.log(5)); }).then(t => console.log(t));请问为什么输出结果是2,5,1呢?
resolve(1)
之后,不应该是优先注册了外部.then
的回调吗?
还是说内部的Promise
是同步执行的?
当然是异步啦,在执行resolve()之前会先将其他语句执行完的
Promise对象,就是将异步操作以同步操作的流程表达出来
我的天。then当然也是同步执行,then方法的参数callback函数才会在then等待状态变化的promise fulfill或者reject后起一个微任务把callback丢进去
这题还不如直接问Promise的原理或写法
.then()当然是同步执行,只不过是.then的cb被放入了微任务队列,产生了异步执行
更详细一点的说法是等到 promise变为 resolved状态的时候,then注册的回调函数才被放入到微任务队列中,等待调用执行
.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);
});
补段代码
做戏做全套... 写一个更有助于理解
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);
})
}
}
}
promise 的构造函数是同步执行的,then方法也是同步执行的,只不过 then 里面的回调函数是异步执行的。
⚠ 同志们,这道题绝对没有你们想象的这么简单,补充一个不错的文章中的摘录:
在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
队列。
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之前执行
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 的回调函数里。这样再分析就简单了。
严格来说then方法 是同步执行的,then方法的参数(回调函数)是 异步执行的
excutor执行器里面是同步执行的,then里面是异步操作
如何体现呢?能举例说明吗?
之所以then是异步是为了处理循环引用的问题。
const p1 = promise1.then(value => {
return p1
})
判断then里面new的promise 跟successCallback执行后得到的p1是不是同一个promise。
但是then里面处理回调的时候new的promise还没有创建完成。
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后面的表达式,然后将其后的代码加入到微任务队列
promise是同步执行的,then也是同步的,只是then中的回调时异步执行的
可以看 promise的 实现
resolve 回调里的内容 会放入 微任务中
then 的回调只是一个list push 动作,
等 resolve 里的 微任务 执行完,就会 调用 then 里的 回调
so 构造器里除了 resolve 之外的 都是 同步执行的(await resolve() 这种情况除外)
then 里的回调是异步执行的