前一段时间找工做,面试了大大小小十几家公司,其中也包含了腾讯、blue等知名公司。总结面试经历,发现本身还有不少不足的地方,许多知识点都知其然不知其因此然。趁着最近事比较少,会陆陆续续总结一些面试的高频知识点,提高本身知识的深度和广度。本文是系列文章之一:promise的理解。javascript
因为JavaScript语言特性,全部程序都是单线程执行的。因为这个特性,JavaScript的一些浏览器事件、请求事件都是异步执行的,经过回调函数处理异步的结果。这是很常见的语法,可是在一些场景下,就会造成回调函数嵌套回调函数,有的状况甚至套用多层,造成了“回调地狱”,这样使得代码臃肿可读性差并且难以维护。php
<!--一个地狱回调的例子,上一个函数的回调结果被下一个函数所依赖-->
const verifyUser = function(username, password, callback){
require.verifyUser(username, password, (error, userInfo) => {
if (error) {
callback(error)
}else{
require.getRoles(userInfo, (error, roles) => {
if (error){
callback(error)
}else {
require.logAccess(roles, (error) => {
if (error){
callback(error);
}else{
callback(null, userInfo, roles);
}
})
}
})
}
})
}
复制代码
为了解决这种问题,社区提出了一些解决方案,采用链式调用的方法,来解决异步回调,并在在ES6被统一成规范。能够说Promise 是异步编程的一种解决方案。前端
做为新的规范,promise采用更加直观也更加易读的方式解决回调嵌套。ES6规定,promise对象是一个构造函数,经过new关键字来生成实例。下面是promise的基本用法java
<!--promise的基本用法-->
const promise = new Promise((resolve, reject) => {
// 异步操做的代码
if (success){
resolve(value);
} else {
reject(error);
}
});
复制代码
Promise构造函数接受一个函数做为参数,该函数的两个参数分别是resolve和reject,他们是JavaScript引擎提供的两个函数。异步操做有两种结果:成功或失败 ios
注意promsie状态 只能由 pending => fulfilled/rejected, 一旦修改就不能再变程序员
那么问题来了,刚才咱们说到promise状态改变后出触发相应的函数,那么咱们处理状态改变的代码要写在哪里呢? 没错,就是then()方法。then方法是定义在原型对象Promise.prototype上的,它的做用是为 Promise 实例添加状态改变时的回调函数then方法的第一个参数是resolved状态的回调函数,第二个参数(可选)是rejected状态的回调函数。面试
<!--promise then方法-->
const promise = new Promise((resolve, reject) => {
resolve('fulfilled'); // 状态由 pending => fulfilled
}).then(result => {
console.log(result); // 'fulfilled'
}, reason => {
})
复制代码
const promise = new Promise((resolve, reject) => {
reject('rejected '); // 状态由 pending => rejected
}).then(result => {
}, reason => {
console.log(reason); // 'rejected'
})
复制代码
上边说过,promise状态一旦修改就不能再变 只能由 pending => fulfilled或者 pending => rejected编程
promise采用链式调用,then()为 Promise 注册回调函数,参数为上一个任务的返回结果,因此链式调用里then 中的函数必定要 return 一个结果或者一个新的 Promise 对象,才可让以后的then 回调接收。axios
const promise = new Promise((resolve, reject) => {
resolve("success")
})
.then(result => {
return result
})
.then(result => {
console.log(result) // "success"
})
.catch(err => {})
复制代码
Promise.prototype.catch方法是.then(null, rejection)或.then(undefined, rejection)的别名,也就是异步操做发生错误时的回调函数,另外,then()方法里的回调函数发生错误也会被catch()捕获。api
<!--promise catch方法-->
const promise = new Promise((resolve, reject) => {
throw new Error('err');
// 或者reject(new Error('err'));
}).then(result => {
console.log(result);
}).catch(err => {
// 处理前两个promise产生的错误
console.log(err)
})
复制代码
到这里,细心的同窗会发现,既然我then()方法第二个参数能够用来抛出错误,干吗还要用这个catch()方法。 其实仍是有区别的,在链式操做里,任何promise抛出的同步或异步错误均可以被then()方法捕获,而reject则处理当前promise的错误。所以,建议不要在then方法里面定义 reject 状态的回调函数(即then的第二个参数),老是使用catch方法,这样也更接近同步的写法(try/catch)。
<!--promise catch方法-->
const promise = new Promise((resolve, reject) => {
// some code
})
// good
promise.then(result => {
// success
}).catch(err => {
// err
})
// not recommend
promise.then(result => {
//success
},err => {
//err
});
复制代码
finally()方法是在ES2018引入标准的,该方法表示promise不管什么状态,在执行完then()或者catch()方法后,最后都会执行finally()方法。
<!-- promise finally方法-->
const promise = new Promise((resolve, reject) => {})
.then(result => {})
.catch(err => {})
.finally(() => {})
复制代码
Promise.all方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。
<!-- promise.all方法-->
const promise1 = new Promise((resolve, reject) => {resolve("promise1")}),
promise2 = new Promise((resolve, reject) => {resolve("promise2")}),
promise3 = new Promise((resolve, reject) => {resolve("promise3")});
Promise.all([promise1,promise2,promise3]).then(data => {
console.log(data);
// ["promise1", "promise2", "promise3"] 结果顺序和promise实例数组顺序是一致的
}).catch(err => {
consolo.log(err)
});
复制代码
只有promise一、promise二、promise3的状态都变成fulfilled,Promise.all的状态才会变成fulfilled,此时promise一、promise二、promise3的返回值组成一个数组,传递给Promise.all的回调函数。
只要promise一、promise二、promise3之中有一个被rejected,Promise.all的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给Promise.all的回调函数
在作项目的时候咱们常常会碰到一个页面要有多个请求,咱们可使用promise.all封装,便于请求管理。
相似的axios也有axios.all()方法处理并发请求
Promise.race方法一样是将多个 Promise 实例,包装成一个新的 Promise 实例。
<!-- promise.race方法-->
const promise = Promise.race([promise1, promise2, promise3]);
复制代码
上面代码中,只要promise一、promise二、promise3之中有一个实例率先改变状态,promise的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给p的回调函数。
项目中咱们常常会遇到须要根据业务将axios再封装的,好比请求拦截设置token以及Content-Type,响应拦截根据不一样的状态码设置不一样的响应。此外咱们还能够将axios再封装
import axios from "./axios"
import qs from "qs";
export default {
get: function(url, params) {
return new Promise((resolve, reject) => {
axios.get(url, {params: params})
.then(res => {
resolve(res)
})
.catch(err => {
console.log(err)
})
})
},
post: function(url, params) {
return new Promise((resolve, reject) => {
axios.post(url, qs.stringify(params))
.then(res => {
resolve(res);
})
.catch(err => {
console.log(err)
})
});
}
}
<!--使用 整个user模块的请求都在此文件管理-->
import require from "@/utils/require"
const user = {
userList() {
return require.post("/api.php", {}).then(res => res.result)
},
userInfo() {
return require.post("/api.php?&uid=20", {}).then(res => res.result)
},
...
}
export default user
复制代码
用promise实现异步加载图片的例子
function loadImageAsync(url) {
return new Promise((resolve, reject) => {
const image = new Image();
image.onload = () => {
resolve(image);
};
image.onerror = () => {
reject(new Error('Could not load image at ' + url));
};
image.src = url;
});
}
const loadImage = loadImageAsync(url);
复制代码
前端技术近年来发展迅速,新技术层出不穷,做为一个程序员er也要有持续学习的觉悟。 欢迎你们关注个人公众号:前端Readhub 。😄😄