一、Promise的理解与使用
抽象表达:
Promise是ES6新增的一种语法,是异步编程的一种解决方案。(旧方案是单纯的使用回调函数)
异步编程:fs文件操作、数据库操作、Ajax、定时器。
具体表达:
(1)从语法上说,promise是一种构造函数
(2)从功能上说,promise 对象用来封装一个异步操作并可以获取其成功/失败的结果值。
1. Promise的状态
三种状态:
pending(进行中,初始状态)
fulfilled(成功状态,也叫resolved)
rejected(失败状态)
Promise只有两个过程(状态):
(1)pending -> fulfilled : Resolved(已完成)
(1)pending -> rejected:Rejected(已拒绝)
通过函数resolve将状态转为fulfilled,函数reject将状态转为rejected,状态一经改变就不可以再次变化。
一个 promise 对象只能改变一次, 无论变为成功还是失败, 都会有一个结果数据 ,成功的结果数据一般称为 value, 失败的结果数据一般称为 reason。
2. Promise的状态可变吗?
一经改变不能再改变。
3. Promise的基本流程
4. Promise的基本使用
function doDelay(timeout){
// 创建一个新的promise对象并返回
return new Promise((resolve, reject) => { // 执行器函数
// 执行异步操作任务
setTimeout(() => {
const time = Date.now()
// 如果当前时间是偶数代表成功,否则失败
if (time % 2 === 0) {
// 如果成功,调用resolve(value)
resolve('成功的数据,time=' + time)
} else {
// 如果失败,调用reject(reason)
reject('失败的数据,time=' + time)
}
}, timeout);
})
}
let promise = doDelay(1000)
//.then() 和执行器(executor)同步执行,.then() 中的回调函数异步执行
promise.then(
value => { // 接收得到成功的value数据 onResolved
console.log('成功的回调', value) // 成功的回调 成功的数据
},
reason => { // 接收得到失败的reason数据 onRejected
console.log('失败的回调', reason) // 失败的回调 失败的数据
}
)
使用promise封装ajax异步请求。
function promiseAjax(url){
// 创建一个新的promise对象并返回
return new Promise((resolve, reject) => { // 执行器函数
const xhr = new XMLHttpRequest();
xhr.onreadystatechange=()=>{
if(xhr.readyState!==4) return;
const {status,response} =xhr;
if(status>=200&&status<300){
resolve(JSON.parse(response))
}else{
reject(new Error("失败,status:"+status))
}
}
xhr.open("GET",url);
xhr.send()
})
}
let promise = promiseAjax('xxx')
promise.then(
data => {
console.log('成功的回调', data)
},
error => {
console.log('失败的回调', error.message)
}
)
二、为什么要用Promise?
特点:
(1)将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。流程更加清晰,代码更加优雅。
(2)Promise对象提供统一的接口,使得控制异步操作更加容易。
缺点:
(1)无法取消Promise,一旦新建它就会立即执行,无法中途取消。
(2)如果不设置回调函数,Promise内部抛出的错误,不会反应到外部。
(3)当处于pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。
1. 指定回调函数的方式更加灵活
promise:启动异步任务 => 返回promise对象 => 给promise对象绑定回调函数(甚至可以在异步任务结束后指定)
2. 支持链式调用,可以解决回调地狱问题
(1)什么是回调地狱?
回调函数嵌套使用,外部回调函数异步执行的结果是嵌套的回调执行条件。具体来说就是异步返回值又依赖于另一个异步返回值(看下面的代码)
doSomething(function(result) {
doSomethingElse(result, function(newResult) {
doThirdThing(newResult, function(finalResult) {
console.log('Got the final result:' + finalResult)
}, failureCallback)
}, failureCallback)
}, failureCallback)
(2)回调地狱的缺点?
不便于阅读、不便于异常处理
(3)解决方案?
promise链式调用
doSomething()
.then(result => doSomethingElse(result))
.then(newResult => doThirdThing(newResult))
.then(finalResult => {console.log('Got the final result:' + finalResult)})
.catch(failureCallback)
(4)终极解决方案async/await
async function request() {
try{
const result = await doSomething()
const newResult = await doSomethingElse(result)
const finalResult = await doThirdThing(newResult)
console.log('Got the final result:' + finalResult)
} catch (error) {
failureCallback(error)
}
}
三、如何使用Promise?
1. Promise 构造函数:Promise(executor) {}
executor 函数:同步执行 (resolve, reject) => {}
resolve 函数:内部定义成功时调用的函数 resove(value)
reject 函数:内部定义失败时调用的函数 reject(reason)
说明:executor 是执行器,会在 Promise 内部立即同步回调,异步操作 resolve/reject 就在 executor 中执行
let p = new Promise((resolve,reject)=>{
console.log(1111)
})
console.log(222)
//输出结果
//111
//222
2. Promise.prototype.then 方法:p.then(onResolved, onRejected)
指定两个回调(成功+失败)
onResolved 函数:成功的回调函数 (value) => {}
onRejected 函数:失败的回调函数 (reason) => {}
说明:指定用于得到成功 value 的成功回调和用于得到失败 reason 的失败回调,返回一个新的 promise 对象。
3. Promise.prototype.catch 方法:p.catch(onRejected)
指定失败的回调
onRejected 函数:失败的回调函数 (reason) => {}
function doDelay(timeout){
// 创建一个新的promise对象并返回
return new Promise((resolve, reject) => { // 执行器函数
// 执行异步操作任务
setTimeout(() => {
const time = Date.now()
// 如果当前时间是偶数代表成功,否则失败
if (time % 2 === 0) {
// 如果成功,调用resolve(value)
resolve('成功的数据,time=' + time)
} else {
// 如果失败,调用reject(reason)
reject('失败的数据,time=' + time)
}
}, timeout);
})
}
let promise = doDelay(1000)
promise.then(
value => { // 接收得到成功的value数据 onResolved
console.log('成功的回调', value) // 成功的回调 成功的数据
},
).catch(
reason => { // 接收得到失败的reason数据 onRejected
console.log('失败的回调', reason) // 失败的回调 失败的数据
}
)
说明:这是then() 的语法糖,相当于 then(undefined, onRejected)
4. Promise.resolve 方法:Promise.resolve(value)
是promise这个函数对象的,并不属于实例对象。
value:将被 Promise 对象解析的参数,也可以是一个成功或失败的 Promise 对象
返回:返回一个带着给定值解析过的 Promise 对象,如果参数本身就是一个 Promise 对象,则直接返回这个 Promise 对象。
作用:快速得到Promise对象,一个封装一个值,将这个值转换为Promise对象
(1)如果传入的参数为 非Promise类型的对象, 则返回的结果为成功promise对象
let p1 = Promise.resolve(521);
console.log(p1); // Promise {<fulfilled>: 521}
(2)如果传入的参数为 Promise 对象, 则参数的结果决定了 resolve 的结果
let p2 = Promise.resolve(new Promise((resolve, reject) => {
// resolve('OK'); // 成功的Promise
reject('Error');
}));
console.log(p2);
p2.catch(reason => {
console.log(reason);
})
5. Promise.reject 方法:Promise.resolve(reason)
reason:失败的原因
说明:返回一个失败的 promise 对象
let p = Promise.reject(521);
let p2 = Promise.reject('iloveyou');
let p3 = Promise.reject(new Promise((resolve, reject) => {
resolve('OK');
}));
console.log(p);
console.log(p2);
console.log(p3);
Promise.resolve()/Promise.reject() 方法就是一个语法糖
用来快速得到Promise对象
6. Promise.all 方法:Promise.all(iterable)
iterable:包含 n 个 promise 的可迭代对象,如 Array 或 String
说明:返回一个新的 promise,只有所有的 promise 都成功才成功,只要有一个失败了就直接失败。
let p1= new Promise((resolve,reject)=>{
resolve("success")
})
let p2 = Promise.resolve('success')
let p3 = Promise.resolve('yet')
let p4 = Promise.reject('error')
const res = Promise.all([p1,p2,p3]);
res.then(
value => {
console.log('res成功的回调', value)
},
reason => {
console.log('res失败的回调', reason)
}
)
const res2 = Promise.all([p1,p2,p3,p4]);
res2.then(//p4失败,所以res2失败
value => {
console.log('res2成功的回调', value)
},
reason => {
console.log('res2失败的回调', reason)
}
)let p1= new Promise((resolve,reject)=>{
resolve("success")
})
let p2 = Promise.resolve('success')
let p3 = Promise.resolve('yet')
let p4 = Promise.reject('error')
const res = Promise.all([p1,p2,p3]);
const res2 = Promise.all([p1,p2,p3,p4]);
console.log(res)
console.log(res2)
7. Promise.race方法:Promise.race(iterable)
iterable:包含 n 个 promise 的可迭代对象,如 Array 或 String
说明:返回一个新的 promise,第一个完成的 promise 的结果状态就是最终的结果状态,谁先完成就输出谁(不管是成功还是失败)
let p1= new Promise((resolve,reject)=>{
setTimeout(() => {
resolve(1)
}, 1000)
})
let p2 = Promise.resolve(2)
let p3 = Promise.resolve(3)
let p4 = Promise.reject(4)
//谁先完成就输出谁(不管是成功还是失败)
const res = Promise.race([p1,p2,p3,p4]);
res.then(//2先执行,输出2
value => {//res成功的回调 2
console.log('res成功的回调', value)
},
reason => {
console.log('res失败的回调', reason)
}
)
const res2 = Promise.all([p4,p2,p3,p1]);
res2.then(//4先执行,输出4
value => {//res2失败的回调 4
console.log('res2成功的回调', value)
},
reason => {
console.log('res2失败的回调', reason)
}
)
8.Promise.any()
区别于Promise.all(), Promise.any() 只要有一个成功,就返回成功的promise,如果没有一个成功,就返回一个失败的promise。
9.Promise.allSelected()
Promise.allSelected([]).then(res => {}).catch(err => {})
返回一个在所有给定的promise都已经fulfilled或rejected后的promise,并带有一个对象数组,每个对象表示对应的promise结果
const promise1 = Promise.resolve(3);
const promise2 = new Promise((resolve, reject) => setTimeout(reject, 100, 'foo'));
const res= Promise.allSettled([promise1,promise2])
res.then(
results=>results.forEach(result=>console.log(result))
)
四、Promise的几个关键问题
1. 如何改变 promise 的状态?
(1)resolve(value):如果当前是 pending 就会变为 resolved
(2)reject(reason):如果当前是 pending 就会变为 rejected
(3)抛出异常:如果当前是 pending 就会变为 rejected
const p = new Promise((resolve, reject) => {
//resolve(1) // promise变为resolved成功状态
//reject(2) // promise变为rejected失败状态
throw new Error('出错了') // 抛出异常,promise变为rejected失败状态,reason为抛出的error
})
p.then(
value => {},
reason => {console.log('reason',reason)}
)
// reason Error:出错了
2. 一个 promise 指定多个成功/失败回调函数,都会调用吗?
当 promise 改变为对应状态时都会调用
const p = new Promise((resolve, reject) => {
resolve(1)
// reject(2)
})
p.then(
value => {console.log('value',value)},
reason => {console.log('reason',reason)}
)
p.then(
value => {console.log('value2',value)},
reason => {console.log('reason2',reason)}
)
// value 1
// value 1
// reason 2
// reason2 2
3. 改变 promise 状态(resolve、reject、throw)和指定回调函数(then,catch)谁先谁后?
都有可能,常规是先指定回调再改变状态,但也可以先改状态再指定回调
如何先改状态再指定回调?
(1)在执行器中直接调用 resolve()/reject()
(2)延迟更长时间才调用 then()
let p = new Promise((resolve, reject) => {
resolve('OK');//执行器函数中的任务是同步任务,直接调动resolve,这是先改变状态,后执行回调
// setTimeout(() => {
//resolve('OK');
// }, 1000); // 有异步就先指定回调,否则先改变状态
});
p.then(value => {
console.log(value);
},reason=>{
})
什么时候才能得到数据?
(1)如果先指定的回调,那当状态发生改变时,回调函数就会调用得到数据
(2)如果先改变的状态,那当指定回调时,回调函数就会调用得到数据
new Promise((resolve, reject) => {
setTimeout(() => {
resolve(1) // 改变状态
}, 1000)
}).then( // 指定回调函数 (先指定)
value => {},
reason =>{}
)
此时,先指定回调函数,保存当前指定的回调函数;后改变状态(同时指定数据),然后异步执行之前保存的回调函数。
new Promise((resolve, reject) => {
resolve(1) // 改变状态
}).then( // 指定回调函数
value => {},
reason =>{}
)
这种写法,先改变的状态(同时指定数据),后指定回调函数(不需要再保存),直接异步执行回调函数
4. promise.then() 返回的新 promise 的结果状态由什么决定?
(1)简单表达:由 then() 指定的回调函数执行的结果决定
let p = new Promise((resolve, reject) => {
resolve('ok');
});
//执行 then 方法
let result = p.then(value => {
console.log(value);
}, reason => {
console.warn(reason);
});
console.log(result);
(2)详细表达:
① 如果抛出异常,新 promise 变为 rejected,reason 为抛出的异常
let p = new Promise((resolve, reject) => {
resolve('ok');
});
//执行 then 方法
let result = p.then(value => {
//1. 抛出错误
throw '出了问题';
}, reason => {
console.warn(reason);
});
console.log(result);
② 如果返回的是非 promise 的任意值,新 promise 变为 resolved,value 为返回的值
let p = new Promise((resolve, reject) => {
resolve('ok');
});
//执行 then 方法
let result = p.then(value => {
//2. 返回结果是非 Promise 类型的对象
return 521;
}, reason => {
console.warn(reason);
});
console.log(result);
③ 如果返回的是另一个新 promise,此 promise 的结果就会成为新 promise 的结果
let p = new Promise((resolve, reject) => {
resolve('ok');
});
//执行 then 方法
let result = p.then(value => {
//3. 返回结果是 Promise 对象
return new Promise((resolve, reject) => {
// resolve('success');
reject('error');
});
}, reason => {
console.warn(reason);
});
console.log(result);
5.promise 如何串联多个操作任务?
(1)promise 的 then() 返回一个新的 promise,可以并成 then() 的链式调用
(2)通过 then 的链式调用串联多个同步/异步任务
let p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('OK');
}, 1000);
});
p.then(value => {
return new Promise((resolve, reject) => {
resolve("success");
});
}).then(value => {
console.log(value); // success
}).then(value => {
console.log(value); // undefined
})
new Promise((resolve, reject) => {
setTimeout(() => {
console.log('执行任务1(异步)')
resolve(1)
}, 1000)
}).then(
value => {
console.log('任务1的结果', value)
console.log('执行任务2(同步)')
return 2 // 同步任务直接return返回结果
}
).then(
value => {
console.log('任务2的结果', value)
return new Promise((resolve, reject) => { // 异步任务需要包裹在Promise对象中
setTimeout(() => {
console.log('执行任务3(异步)')
resolve(3)
}, 1000)
})
}
).then(
value => {
console.log('任务3的结果', value)
}
)
// 执行任务1(异步)
// 任务1的结果 1
// 执行任务2(同步)
// 任务2的结果 2
// 执行任务3(异步)
// 任务3的结果 3
6.Promise 异常穿透(传透)?
在最后加一个catch方法
(1)当使用 promise 的 then 链式调用时,可以在最后指定失败的回调
(2)前面任何操作出了异常,都会传到最后失败的回调中处理
new Promise((resolve, reject) => {
//resolve(1)
reject(1)
}).then(
value => {
console.log('onResolved1()', value)
return 2
}
).then(
value => {
console.log('onResolved2()', value)
return 3
}
).then(
value => {
console.log('onResolved3()', value)
}
).catch(
reason => {
console.log('onRejected1()', reason)
}
)
// onRejected1() 1
相当于这种写法:多写了很多reason => {throw reason}
new Promise((resolve, reject) => {
//resolve(1)
reject(1)
}).then(
value => {
console.log('onResolved1()', value)
return 2
},
reason => {throw reason} // 抛出失败的结果reason
).then(
value => {
console.log('onResolved2()', value)
return 3
},
reason => {throw reason} // 抛出失败的结果reason
).then(
value => {
console.log('onResolved3()', value)
},
reason => {throw reason} // 抛出失败的结果reason
).catch(
reason => {
console.log('onRejected1()', reason)
}
)
// onRejected1() 1
7.中断 promise 链?
当使用 promise 的 then 链式调用时,在中间中断,不再调用后面的回调函数
办法:在回调函数中返回一个 pending 状态的 promise 对象,return new Promise(() => {})
new Promise((resolve, reject) => {
//resolve(1)
reject(1)
}).then(
value => {
console.log('onResolved1()', value)
return 2
}
).then(
value => {
console.log('onResolved2()', value)
return 3
}
).then(
value => {
console.log('onResolved3()', value)
}
).catch(
reason => {
console.log('onRejected1()', reason)
}
).then(
value => {
console.log('onResolved4()', value)
},
reason => {
console.log('onRejected2()', reason)
}
)
// onRejected1() 1
// onResolved4() undefined
为了在 catch 中就中断执行,可以这样写,return new Promise(() => {}) :
new Promise((resolve, reject) => {
//resolve(1)
reject(1)
}).then(
value => {
console.log('onResolved1()', value)
return 2
}
).then(
value => {
console.log('onResolved2()', value)
return 3
}
).then(
value => {
console.log('onResolved3()', value)
}
).catch(
reason => {
console.log('onRejected1()', reason)
return new Promise(() => {}) // 返回一个pending的promise
}
).then(
value => {
console.log('onResolved4()', value)
},
reason => {
console.log('onRejected2()', reason)
}
)
// onRejected1() 1
在 catch 中返回一个新的 promise,且这个 promise 没有结果。
由于,返回的新的 promise 结果决定了后面 then 中的结果,所以后面的 then 中也没有结果。
这就实现了中断 promise链的效果。
五、 async await 和Promise
1.执行async函数,返回的都是Promise对象
async function test1() {
return 1;
};
async function test2() {
return Promise.resolve(1);
};
const res1 = test1();
const res2 = test2();
console.log('res1', res1);
console.log('res2', res2);
2.Promise.then 成功的情况,对应的是await
async function test3() {
const p3 = Promise.resolve(3);
p3.then(data => {
console.log('data',data);
});
const data = await p3;
console.log(data);
}
test3();
//data 3
//3
3.Promise.catch 异常的情况, 对于try…catch…
async function test3() {
const p3 = Promise.reject(3);
p3.catch(data => {
console.log('data',data);
});
try {
const data = await p3;
console.log('data',data);
} catch (err) {
console.log('err', err);
}
}
test3();
//data 3
// error 3
4. 如何让Promise顺序执行?应用场景是什么?
const p1 = new Promise(resolve => {
setTimeout(()=> {
resolve(1);
}, 3000);
});
const p2 = new Promise(resolve => {
setTimeout(()=> {
resolve(2);
}, 2000);
});
const p3 = new Promise(resolve => {
setTimeout(()=> {
resolve(3);
}, 1000);
});
async function execute() {
await p1.then(data => console.log(data));
await p2.then(data => console.log(data));
await p3.then(data => console.log(data));
};
execute();
//1
//2
//3
六、常见面试题
Promise面试题汇总_CUG-GZ的博客-CSDN博客_promise面试题