当前位置: 首页 > news >正文

【前端】手写代码输出题易错点汇总

两天更新完。

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

promise.then 是微任务,它会在所有的宏任务执行完之后才会执行,同时需要promise内部的状态发生变化,因为这里内部没有发生变化,一直处于pending状态,所以不输出3。

const promise1 = new Promise((resolve, reject) => {console.log('promise1')resolve('resolve1')
})
const promise2 = promise1.then(res => {console.log(res)
})
console.log('1', promise1);
console.log('2', promise2);
/*
promise1
1 Promise{<resolved>: resolve1}
2 Promise{<pending>}
resolve1
*/

直接打印Promise,会打印出它的状态值和参数。

const promise = new Promise((resolve, reject) => {console.log(1);setTimeout(() => {console.log("timerStart");resolve("success");console.log("timerEnd");}, 0);console.log(2);
});
promise.then((res) => {console.log(res);
});
console.log(4);
/*
1
2
4
timerStart
timerEnd
success
*/Promise.resolve().then(() => {console.log('promise1');const timer2 = setTimeout(() => {console.log('timer2')}, 0)
});
const timer1 = setTimeout(() => {console.log('timer1')Promise.resolve().then(() => {console.log('promise2')})
}, 0)
console.log('start');
/*
start
promise1
timer1
promise2
timer2
*/

代码执行过程如下:

  1. 首先,Promise.resolve().then是一个微任务,加入微任务队列
  2. 执行timer1,它是一个宏任务,加入宏任务队列
  3. 继续执行下面的同步代码,打印出start
  4. 这样第一轮宏任务就执行完了,开始执行微任务Promise.resolve().then,打印出promise1
  5. 遇到timer2,它是一个宏任务,将其加入宏任务队列,此时宏任务队列有两个任务,分别是timer1、timer2;
  6. 这样第一轮微任务就执行完了,开始执行第二轮宏任务,首先执行定时器timer1,打印timer1;
  7. 遇到Promise.resolve().then,它是一个微任务,加入微任务队列
  8. 开始执行微任务队列中的任务,打印promise2;
  9. 最后执行宏任务timer2定时器,打印出timer2;
const promise = new Promise((resolve, reject) => {resolve('success1');reject('error');resolve('success2');
});
promise.then((res) => {console.log('then:', res);
}).catch((err) => {console.log('catch:', err);
})
/*
then:success1
考察的就是Promise的状态在发生变化之后,就不会再发生变化
*/Promise.resolve(1).then(2).then(Promise.resolve(3)).then(console.log)
/*
1
*/

then方法接受的参数是函数,而如果传递的并非是一个函数,这就会导致前一个Promise的结果会传递下面。Promise.resolve(3) 是一个 Promise 对象,不是一个函数。所以这个参数也被忽略。

const promise1 = new Promise((resolve, reject) => {setTimeout(() => {resolve('success')//状态改变}, 1000)
})
const promise2 = promise1.then(() => {//注意这是promise2throw new Error('error!!!')
})
console.log('promise1', promise1)
console.log('promise2', promise2)
setTimeout(() => {console.log('promise1', promise1)console.log('promise2', promise2)
}, 2000)
/*
promise1 Promise {<pending>}
promise2 Promise {<pending>}
promise1 Promise {<fulfilled>: "success"}
promise2 Promise {<rejected>: Error: error!!}
*/Promise.resolve(1).then(res => {console.log(res);//输出1return 2;//给到了最后的.then}).catch(err => {return 3;}).then(res => {console.log(res);//输出2});
/*
1   
2
*/

Promise是可以链式调用的,由于每次调用 .then 或者 .catch 都会返回一个新的 promise,从而实现了链式调用, 它并不像一般任务的链式调用一样return this。
上面的输出结果之所以依次打印出1和2,是因为resolve(1)之后走的是第一个then方法,并没有进catch里,所以第二个then中的res得到的实际上是第一个then的返回值。并且return 2会被包装成resolve(2),被最后的then打印输出2。

Promise.resolve(1).then(2).then(Promise.resolve(3)).then(console.log)//1

只需要记住一个原则:.then 或.catch 的参数期望是函数,传入非函数则会发生值透传。就是当它不存在。
第一个then和第二个then中传入的都不是函数,一个是数字,一个是对象,因此发生了透传,将resolve(1) 的值直接传到最后一个then里,直接打印出1。

Promise.resolve().then(() => {return new Error('error!!!')
}).then(res => {console.log("then: ", res)
}).catch(err => {console.log("catch: ", err)
})
/*
"then: " "Error: error!!!"
*/

返回任意一个非 promise 的值都会被包裹成 promise 对象,因此这里的return new Error(‘error!!!’)也被包裹成了return Promise.resolve(new Error(‘error!!!’)),因此它会被then捕获而不是catch。

const promise = Promise.resolve().then(() => {return promise;
})
promise.catch(console.err)
/*
Uncaught (in promise) TypeError: Chaining cycle detected for promise #<Promise>
*/

这里其实是一个坑,.then 或 .catch 返回的值不能是 promise 本身,否则会造成死循环。

Promise.reject('err!!!').then((res) => {console.log('success', res)}, (err) => {console.log('error', err)}).catch(err => {console.log('catch', err)})//error err!!!

错误直接被then的第二个参数捕获了,所以就不会被catch捕获了

Promise.resolve('1').then(res => {console.log(res)}).finally(() => {console.log('finally')})
Promise.resolve('2').finally(() => {console.log('finally2')return '我是finally2返回的值'}).then(res => {console.log('finally2后面的then函数', res)})
/* 注意一下微任务队列顺序,Promise.resolve是同步代码 因为创建了一个promise
1
finally2
finally
finally2后面的then函数 2
*/function runAsync (x) {const p = new Promise(r => setTimeout(() => r(x, console.log(x)), 1000))return p
}Promise.all([runAsync(1), runAsync(2), runAsync(3)]).then(res => console.log(res))
/*
1
2
3
[1, 2, 3]
*/function runAsync (x) {const p = new Promise(r => setTimeout(() => r(x, console.log(x)), 1000))return p
}
function runReject (x) {const p = new Promise((res, rej) => setTimeout(() => rej(`Error: ${x}`, console.log(x)), 1000 * x))
/*
`Error: ${x}`并不会打印,作为rejected 的原因传入
*/return p
}
Promise.all([runAsync(1), runReject(4), runAsync(3), runReject(2)]).then(res => console.log(res)).catch(err => console.log(err))
/*
// 1s后输出
1
3
// 2s后输出
2
Error: 2 //进入catch
// 4s后输出
4
*/function runAsync(x) {const p = new Promise(r =>setTimeout(() => r(x, console.log(x)), 1000));return p;
}
function runReject(x) {const p = new Promise((res, rej) =>setTimeout(() => rej(`Error: ${x}`, console.log(x)), 1000 * x));return p;
}
Promise.race([runReject(0), runAsync(1), runAsync(2), runAsync(3)]).then(res => console.log("result: ", res)).catch(err => console.log(err));
/* 虽然race只捕获一次,但settimeout回调函数输出还是有的
0
Error: 0
1
2
3
*/async function async1() {console.log("async1 start");await async2();console.log("async1 end");
}
async function async2() {console.log("async2");
}
async1();
console.log('start')
/*
async1 start
async2
start
async1 end跳出async1函数后,执行同步代码start;在一轮宏任务全部执行完之后,再来执行await后面的内容async1 end。
await后面的语句相当于放到了new Promise中,下一行及之后的语句相当于放在Promise.then中。
*/async function async1() {console.log("async1 start");await async2();console.log("async1 end");setTimeout(() => {console.log('timer1')}, 0)
}
async function async2() {setTimeout(() => {console.log('timer2')}, 0)console.log("async2");
}
async1();
setTimeout(() => {console.log('timer3')
}, 0)
console.log("start")
/*
async1 start
async2
start
async1 end
timer2
timer3
timer1
*/

代码的执行过程如下:

  1. 首先进入async1,打印出async1 start;
  2. 之后遇到async2,进入async2,遇到定时器timer2,加入宏任务队列,之后打印async2;
  3. 由于async2阻塞了后面代码的执行,所以执行后面的定时器timer3,将其加入宏任务队列,之后打印start;
  4. 然后执行async2后面的代码,打印出async1 end,遇到定时器timer1,将其加入宏任务队列;
  5. 最后,宏任务队列有三个任务,先后顺序为timer2,timer3,timer1,没有微任务,所以直接所有的宏任务按照先进先出的原则执行。
async function async1 () {console.log('async1 start');await new Promise(resolve => {console.log('promise1')})console.log('async1 success');return 'async1 end'
}
console.log('srcipt start')
async1().then(res => console.log(res))
console.log('srcipt end')
/*
script start
async1 start
promise1
script end
async1中await后面的Promise是没有返回值的,也就是它的状态始终是pending状态,所以在await之后的内容是不会执行的,包括async1后面的 .then。
*/

相关文章:

  • git检查提交分支和package.json的version版本是否一致
  • 使用vue2开发一个医疗预约挂号平台-前端静态网站项目练习
  • ASP.NET MVC​ 入门指南
  • JAVA设计模式——(六)装饰模式(Decorator Pattern)
  • 建造者模式:分步构建复杂对象的设计模式
  • 罗伯·派克:Go语言创始者的极客人生
  • 【项目管理】进度网络图 笔记
  • Vue 2 的响应式 API 和 Vue 3 的组合式 API 的详细对比,从核心机制、使用方式、代码示例及优缺点展开
  • Linux:git和gdb/cgdb
  • 多线程(线程安全)
  • MacOS上如何运行内网穿透详细教程
  • Puter部署指南:基于Docker的多功能个人云平台掌控自己的数据
  • 《Pinia 从入门到精通》Vue 3 官方状态管理 -- 进阶使用篇
  • 音视频之H.265/HEVC量化
  • Streamlit从入门到精通:构建数据应用的利器
  • CGAL 网格等高线计算
  • 参考文献新国标GB/T 7714-2025的 biblatex 实现
  • CF每日4题
  • 云智融合普惠大模型AI,政务服务重构数智化路径
  • openwrt作旁路由时的几个常见问题 openwrt作为旁路由配置zerotier 图文讲解
  • “茉上茶田”傍大牌诱导加盟续:违规从事特许经营被罚没670余万元
  • 联手华为猛攻主流市场,上汽集团总裁:上汽不做生态孤岛
  • 质与量齐升、快与稳并举,专家解读上海一季度经济数据
  • 《哪吒2》再次延映至五月底,春节档影片仍有竞争力
  • 神舟二十号载人飞行任务新闻发布会将于4月23日上午召开
  • 经济日报刊文:如何破除“内卷式”竞争