文章目录
1. 必备知识点
1. 函数对象&实例对象
将函数作为对象使用(调用函数的属性和方法),简称函数对象
【括号左边是函数,点的左边是函数对象】
new 函数产生的对象,称为实例对象,简称对象。
2. 链式调用
想要不断的调用(链式调用),需要返回自己,或者返回一个和自己类似的结构。
//方式一:返回自己
class Test1{
then(){
console.log(6666);
return this;
}
}
var a= new Test1();
a.then().then().then()
//方式二:一个和自己类似的结构
//promise采用这种方式,因为一个promise只能改变一次状态,要么从pending转化为fulfilled,要么从pending转化为rejected.
class Test2{
then(){
console.log(77777);
return new Test2();
}
}
var b= new Test2();
b.then().then().then()
3. 回调函数的分类
<script>
//1.同步回调函数
const arr = [1,2,3,5];
//forEach()中的回调函数为同步回调函数
arr.forEach(item =>{
console.log(item);
})
console.log("forEach()之后");
/*
* 执行结果:
* 1
* 2
* 3
* 4
* forEach()之后
*/
//2.异步回调函数
//setTimeout()中的回调函数为异步回调函数
setTimeout(()=>{
console.log("timout callback()");
},0);
console.log("setTimeout()之后");
/*
* 执行结果
* setTimeout()之后
* timout callback()
*/
</script>
4. 错误和错误处理
<script>
//抛出异常:throw error
function something(){
if(Date.now()%2 ===1){
console.log("当前时间为奇数,可以执行任务");
}else{
throw new Error("当前时间为偶数,无法执行任务");
}
}
//捕获异常处理
try{
something();
}catch(error){
alert(error.message);
}
</script>
2. promise是什么?
promise是es6中用来解决异步编程的一种方式。通过链式调用的方式, 可以解决回调地狱问题。项目中常用的axios就是把ajax使用promise封装了一下。
3.promiseA+规范及应⽤
4. 为什么使用promise?
promise支持链式调用, 可以解决回调地狱问题
- 什么是回调地狱?
回调函数嵌套调用, 外部回调函数异步执行的结果是嵌套的回调函数执行的条件 - 回调地狱的缺点?
不便于阅读 / 不便于异常处理
解决方案? promise链式调用
终极解决方案? async/await//1、查出当前用户信息 //2、按照当前用户的id查出他的课程 //3、按照当前课程id查出分数 // 回调地狱: $.ajax({ url: "./mock/user.json", success(data) { console.log("查询用户:", data); $.ajax({ url: `./mock/user_corse_${data.id}.json`, success(data) { console.log("查询到课程:", data); $.ajax({ url: `./mock/corse_score_${data.id}.json`, success(data) { console.log("查询到分数:", data); }, error(error) { console.log("出现异常了:" + error); } }); }, error(error) { console.log("出现异常了:" + error); } }); }, error(error) { console.log("出现异常了:" + error); } }); // promise调用方式 function get(url, data) { return new Promise((resolve, reject) => { $.ajax({ url: url, data: data, success: function (data) { resolve(data); }, error: function (err) { reject(err) } }) }); } //调用封装后的方法 get("./mock/user.json") .then((data) => { console.log("用户查询成功~~~:", data) return get(`./mock/user_corse_${data.id}.json`); }) .then((data) => { console.log("课程查询成功~~~:", data) return get(`./mock/corse_score_${data.id}.json`); }) .then((data) => { console.log("课程成绩查询成功~~~:", data) }) .catch((err) => { console.log("出现异常", err) }); // Promise操作明显更加符合我们人类的习惯,类似于流水线式的操作,对于异常的处理更加友好 // 测试代码在promise-test.html中
promise面试题
-
使用Promise实现红绿灯交替重复亮【面试真题】
/* 红灯3秒亮一次,黄灯2秒亮一次,绿灯1秒亮一次;如何让三个灯不断交替重复亮灯?(用Promise实现) */ function red() { console.log("red"); } function green() { console.log("green"); } function yellow() { console.log("yellow"); } function light(cb, timer) { return new Promise(resolve => { setTimeout(() => { cb(); resolve() }, timer); }) } function step() { Promise.resolve().then(() => { return light(red, 3000) }).then(() => { return light(green, 2000) }).then(() => { return light(yellow, 1000) }).finally(() => { // 递归实现循环 return step() }) } step();
-
promise实现sleep函数【面试题】
//Promise实现Sleep函数 function sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } async function test() { console.log(new Date()); await sleep(3000); console.log(new Date()); } test();
-
实现一个带并发限制的异步调度器 Scheduler,保证同时运行的任务最多有两个
// JS 实现一个带并发限制的异步调度器 Scheduler,保证同时运行的任务最多有两个 class Scheduler { constructor(limit) { this.limit = limit this.queue = [] this.count = 0 } add(time, str) { const doTask = () => { return new Promise(resolve => { setTimeout(() => { console.log(str) resolve() }, time) }) } this.queue.push(doTask) } taskStart() { for(let i = 0; i < this.limit; i++) { this.request() } } request() { if (!this.queue.length || this.count > this.limit) { return } this.count++ this.queue.shift()().then(() => { this.count-- this.request() }) } } let kk = new Scheduler(2) kk.add(1000,"1"); kk.add(500,"2"); kk.add(300,"3"); kk.add(400,"4"); kk.taskStart() // 2 3 1 4
-
将图片的加载写成一个Promise,一旦加载完成,Promise的状态就发生变化
const preloadImage = function (path) { return new Promise(function (resolve, reject) { const image = new Image(); image.onload = resolve; image.onerror = reject; image.src = path; }); };
5. async function 与 await expression
-
async function 函数名() { // 。。。}
调用async 函数返回结果是一个Promise对象,promise对象的结果由async函数执行的返回值决定// async函数返回一个promise对象 async function fn1() { return 1 //throw 2 //return Promise.resolve(3) /*return new Promise((resolve, reject)=>{ setTimeout(()=>{ resolve(4) }, 1000) })*/ } //调用async函数 const result = fn1() result.then( value => { console.log('onFulfilled()', value) }, reason => { console.log('onRejected()', reason) } )
-
await expression (value or Promise)
expression一般是Promise对象,也可以是其他值
如果是Promise对象,await返回的是Promise对象成功的值, 如果await的Promise失败,就会抛出异常,需通过try…catch…捕获处理
如果是其他值,直接将此值作为await的返回值<script> function fn2(){ return new Promise((resolve, reject)=>{ //resolve(4) reject(6) }) } async function fn3(){ try{ // await右侧表达式为Promise,得到的结果就是promise成功的value, const value = await fn2() console.log('fn5 value', value) // fn5 value 4 }catch(error){ //如果await右侧的Promise失败,就会直接抛出异常,需通过catch捕获处理进行处理 console.log('fn5 error', error) // fn5 error 6 } } fn3() function fn4(){ return 5 } async function fn5(){ //await右侧表达式不为Promise,得到的结果就是它本身 const value = await fn4() console.log('value', value)// value 5 } fn5() </script>
-
await expression必须写在async function 中,但async function可以没有await expression
-
async函数可以理解为内置自动执行器的Generator函数语法糖,它配合Promise近乎完美的实现了异步编程解决方案
async function async1(){ console.log('async1 start...'); await async2(); //await后面的代码,都可以看作是放在一个回调函数里的代码,是异步的,放在微任务队列中 console.log('async1 end...') } async function async2(){ console.log('async2'); } console.log('start'); async1(); console.log('end'); /* * 执行结果: * start * async1 start... * async2 * end * async1 end... */
6. 宏队列与微队列
-
为什么要引入微任务的方式来进行回调操作,只有宏任务不行吗?
任务队列里的任务遵循先进先出的原则,若当前队列非常长,一些优先级比较高的回调放在进行宏任务队列的队尾,那么该回调迟迟得不到执行,造成的效果就是应用卡顿。所以就引入了微任务,来存放优先级更高的任务。 -
-
js执行顺序:
- 渲染DOM
- 执行宏队列的宏任务
- 执行本周期中的微任务
- 循环以上三步
//例1: <script> setTimeout(()=>{ //立即放入宏队列 console.log('timeout callback1()') Promise.resolve(3).then( value => { //立即放入微队列 console.log('Promise onResolved3()', value) } ) },0) setTimeout(()=>{ //立即放入宏队列 console.log('timeout callback2()') },0) Promise.resolve(1).then( value => { //立即放入微队列 console.log('Promise onResolved1()', value) setTimeout(()=>{ console.log('timeout callback3()', value) },0) } ) Promise.resolve(2).then( value => { //立即放入微队列 console.log('Promise onResolved2()', value) } ) /* * 执行结果 * Promise onResolved1() 1 * Promise onResolved2() 2 * timeout callback1() * Promise onResolved3() 3 * timeout callback2() * timeout callback3() 1 */ </script> //例2: <script> Promise.resolve() .then(() => { console.log("then1"); Promise.resolve() .then(() => { console.log("then1-1"); return 1; }) .then(() => { console.log("then1-2"); }); }) .then(() => { console.log("then2"); }) .then(() => { console.log("then3"); }) /*执行结果: * then1 * then1-1 * then2 * then1-2 * then3 */ /* 第一次 resolve 后第一个 then 的回调进入微任务队列并执行,打印 then1 第二次 resolve 后内部第一个 then 的回调进入微任务队列,此时外部第一个 then 的回调全部执行完毕,需要将外部的第二个 then 回调也插入微任务队列。 执行微任务,打印 then1-1 和 then2,然后分别再将之后 then 中的回调插入微任务队列 执行微任务,打印 then1-2 和 then3 */ </script>