promise
Promise是一种异步编程的解决方案, 异步抽象处理对象
-
三种状态,pending(进行中)、resolved(已完成)、rejected(已失败)
-
一旦状态改变,就不会再变,任何时候都可以得到这个结果
-
可传入函数作为参数,函数的参数:resolve, reject
-
回调函数: .then, .catch, .finally
-
.then:异步执行的
-
.catch:在链式写法中可以捕获前面then中发送的异常
- 都会返回一个新的Promis
- 返回的值不能是 promise 本身,否则会造成死循环
- 参数期望是函数,传入非函数则会发生值穿透
-
.finally: 返回一个Promise,他在Promise结束的时候,无论结果为resolved还是rejected,都会执行里面的回调函数
-
缺点:
1. 无法取消 Promise,一旦新建它就会立即执行,无法中途取消
2. 内部抛出的错误,不会反应到外部
3. 当处于 Pending 状态时,无法得知目前进展到哪一个阶段
解决:
1. 回调地狱
2. 支持多个并发的请求,获取并发请求中的数据
方法
- promise.all[{}]: 等待一个结果返回才会执行下一个(根据看谁跑的慢的原则)
- 如果有一个函数执行为reject,那么全部状态都为rejected
- 会等全部函数执行完毕,同时返回
- promise.race[{}]:哪个结果获得的快,就返回那个结果(根据看谁跑的快的原则)
注意
Promise.resolve(1)
.then(2)
.then(Promise.resolve(3))
.then(console.log)
输出结果:1
解题思路:Promise的then方法的参数期望是函数,传入非函数则会发生值穿透。
all
// then方法不会被执行, catch方法将会被执行,输出结果为:2
var p1 = Promise.resolve(1),
p2 = Promise.reject(2),
p3 = Promise.resolve(3);
Promise.all([p1, p2, p3]).then((res)=>{
console.log(results);
}).catch((err)=>{
console.log(err);
});
// 10s之后输出[1,10,5]
let p1 = new Promise((resolve)=>{
setTimeout(()=>{
console.log('1s') //1s后输出
resolve(1)
},1000)
})
let p10 = new Promise((resolve)=>{
setTimeout(()=>{
console.log('10s') //10s后输出
resolve(10)
},10000)
})
let p5 = new Promise((resolve)=>{
setTimeout(()=>{
console.log('5s') //5s后输出
resolve(5)
},5000)
})
Promise.all([p1, p10, p5]).then((res)=>{
console.log(res); // 最后输出
})
race
- 数组中的元素实例那个率先改变状态,就向下传递谁的状态和异步结果。但是,其余的还是会继续进行的
// 结果: 1s, 1, 5s, 10s
let p1 = new Promise((resolve)=>{
setTimeout(()=>{
console.log('1s') //1s后输出
resolve(1)
},1000)
})
let p10 = new Promise((resolve)=>{
setTimeout(()=>{
console.log('10s') //10s后输出
resolve(10) //不传递
},10000)
})
let p5 = new Promise((resolve)=>{
setTimeout(()=>{
console.log('5s') //5s后输出
resolve(5) //不传递
},5000)
})
Promise.race([p1, p10, p5]).then((res)=>{
console.log(res); // 最后输出
})
race: 请求某个图片资源(超时的操作)
//请求某个图片资源
let requestImg = new Promise(function(resolve, reject){
var img = new Image();
img.onload = function(){
resolve(img);
}
});
//延时函数,用于给请求计时
let timeOut = new Promise(function(resolve, reject){
setTimeout(function(){
reject('图片请求超时');
}, 5000);
});
Promise.race([requestImg, timeout]).then((res)=>{
console.log(res);
}).catch((err)=>{
console.log(err);
});
异步回调Async/Await与Promise区别
async/await
- 基于Promise实现的,它不能用于普通的回调函数,是非阻塞的
- 使得异步代码看起来像同步代码
- 函数前添加了async关键字,+await关键字只能用在aync定义的函数内
- 返回值必定是 promise 对象
- await等的是右侧表达式的结果
优点:
- 节约了代码量
- 不需要写.then,替代then
- 不需要写匿名函数处理Promise的resolve值
- 不需要定义多余的data变量,还避免了嵌套代码
promise:
const makeRequest = () => {
try {
getJSON().then(result => {
const data = JSON.parse(result)
console.log(data)
})
} catch (err) {
console.log(err)
}
}
aync/await:
const makeRequest = async () => {
try {
const data = JSON.parse(await getJSON())
console.log(data)
} catch (err) {
console.log(err)
}
}
Promise相关的面试题
demo_1
const promise = new Promise((resolve, reject) => {
console.log(1);
resolve();
console.log(2);
});
promise.then(() => {
console.log(3);
});
console.log(4);
输出结果为:1,2,4,3
demo_2
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('success')
reject('error')
}, 1000)
})
promise.then((res)=>{
console.log(res)
},(err)=>{
console.log(err)
})
输出结果:success
解题思路:Promise状态一旦改变,无法在发生变更
demo_3
Promise.resolve(1)
.then(2)
.then(Promise.resolve(3))
.then(console.log)
输出结果:1 , Promise {<fulfilled>: undefined}
解题思路:Promise的.then()的参数期望是函数,传入非函数则会发生值穿透
demo_4
setTimeout(()=>{
console.log('setTimeout')
})
let p1 = new Promise((resolve)=>{
console.log('Promise1')
resolve('Promise2')
})
p1.then((res)=>{
console.log(res)
})
console.log(1)
输出结果:Promise1, 1, Promise2, setTimeout
demo_5
Promise.resolve(1)
.then((res) => {
console.log('res', res);
return 2;
})
.catch((err) => {
return 3;
})
.then((res) => {
console.log('res2', res);
});
输出结果:res 1, res2 2
解题思路:
Promise首先resolve(1),接着就会执行then函数,因此会输出1
因为是resolve函数,因此后面的catch函数不会执行,直接执行第二个then函数,
return 2会被包装成resolve(2), 因此会输出2
Promise.reject(1)
.then(res => {
console.log('res', res);
return 2;
})
.catch(err => {
console.log('err', err);
return 3
})
.then(res => {
console.log('res2', res);
});
// err 1, res2 3
demo_6
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
console.log('开始');
resolve('success');
}, 5000);
});
const start = Date.now();
promise.then((res) => {
console.log(res, Date.now() - start);
});
promise.then((res) => {
console.log(res, Date.now() - start);
});
输出结果:开始, success 5002, success 5002
解题思路:
promise 的.then或者.catch可以被调用多次,但这里 Promise 构造函数只执行一次。
或者说 promise 内部状态一经改变,并且有了一个值,那么后续每次调用.then 或者.catch都会直接拿到该值
demo_7
let p1 = new Promise((resolve,reject)=>{
let num = 6
if(num<5){
console.log('resolve1')
resolve(num)
}else{
console.log('reject1')
reject(num)
}
})
p1.then((res)=>{
// p1 执行reject(num), 所以不会执行此段代码
console.log('resolve2')
console.log(res)
},(rej)=>{
console.log('reject2')
let p2 = new Promise((resolve,reject)=>{
if(rej*2>10){
console.log('resolve3')
resolve(rej*2)
}else{
console.log('reject3')
reject(rej*2)
}
})
return p2
}).then((res)=>{
console.log('resolve4')
console.log(res)
},(rej)=>{
console.log('reject4')
console.log(rej)
})
输出结果:reject1, reject2, resolve3, resolve4, 12
解题思路:我们上面说了Promise的先进之处在于可以在then方法中继续写Promise对象并返回。
demo_8
const promise = new Promise((resolve, reject) => {
reject("error");
resolve("success2");
});
promise.then(res => {
console.log("then1: ", res);
}).then(res => {
console.log("then2: ", res);
}).catch(err => {
console.log("catch: ", err);
}).then(res => {
console.log("then3: ", res);
})
// catch: error, then3: undefined
demo_9
Promise.resolve().then(() => {
returnnewError('error!!!')
}).then(res => {
console.log("then: ", res)
}).catch(err => {
console.log("catch: ", err)
})
// "then: " "Error: error!!!"
const promise = Promise.resolve().then(() => {
return promise;
})
promise.catch(console.err)
.then 或 .catch 返回的值不能是 promise 本身,否则会造成死循环。
// err: Uncaught (in promise) TypeError: Chaining cycle detected for promise #<Promise>
- 实现一个简单的Promise
function Promise(fn){
var status = 'pending'
function successNotify(){
status = 'fulfilled'//状态变为fulfilled
toDoThen.apply(undefined, arguments)//执行回调
}
function failNotify(){
status = 'rejected'//状态变为rejected
toDoThen.apply(undefined, arguments)//执行回调
}
function toDoThen(){
setTimeout(()=>{ // 保证回调是异步执行的
if(status === 'fulfilled'){
for(let i =0; i< successArray.length;i ++) {
successArray[i].apply(undefined, arguments)//执行then里面的回掉函数
}
}else if(status === 'rejected'){
for(let i =0; i< failArray.length;i ++) {
failArray[i].apply(undefined, arguments)//执行then里面的回掉函数
}
}
})
}
var successArray = []
var failArray = []
fn.call(undefined, successNotify, failNotify)
return {
then: function(successFn, failFn){
successArray.push(successFn)
failArray.push(failFn)
return undefined // 此处应该返回一个Promise
}
}
}
解题思路:Promise中的resolve和reject用于改变Promise的状态和传参,then中的参数必须是作为回调执行的函数。因此,当Promise改变状态之后会调用回调函数,根据状态的不同选择需要执行的回调函数。