
在处理一些需要花费比较长时间的任务时,使用Promise就可以进行异步的处理,防止阻塞。实现把异步当同步写。
解决回调地狱问题,而且因为有了resolve和reject可以进行异步处理并且得知任务进度
异步任务不进入主线程,而是进入异步队列,前一个任务是否执行完毕不影响下一个任务的执行。
setTimeout(function(){
console.log('我在上面')
},1000}
console.log('我在下面')
执行结果:
我在下面
我在上面
>
这种不阻塞后面任务执行的任务就叫异步任务
上面说到,异步任务不能保证按照顺序执行,但实际上我们是有这样的需求的。在没有promise的时候我们这样子实现。
setTimeout(function(){
console.log("获取id");
setTimeout(function(){
console.log("获取用户名");
setTimeout(function(){
console.log("获取email");
},2000)
},1000)
},3000)
可以看到,代码中的回调函数套回调函数,居然套了3层,这种回调函数中嵌套回调函数的情况就叫做回调地狱。
总结一下,回调地狱就是为是实现代码顺序执行而出现的一种操作,它会造成我们的代码可读性非常差,后期不好维护。
new Promise((resolve,reject)=>{})
Promise接受一个函数作为参数
函数参数中接受俩个形参参数
控制台打印Promise实例,他有两个属性
第一种状态:pending (准备,待解决,进行中)
第二种状态:fulfilled (已完成,成功)
第三种状态:rejected (已拒绝,失败)
通过调用resolve()和reject()改变当前promise对象的状态
示例
const p = new Promise((resolve,reject)=>{
resolve()
})
console.log(p)// [[PromiseState]]: "fulfilled"
const p = new Promise((resolve,reject)=>{
reject()
})
console.log(p)// [[PromiseState]]: "rejected"
promise状态的改变是一次性的。 改状态只能改一次,不是fulfilled就是rejected
通过调用resolve()或reject()传递参数,改变当前promise对象的结果
const p = new Promise((resolve,reject)=>{
resolve('resolve的参数')
})
console.log(p)// [[PromiseResult]]: "resolve的参数"
参数是两个回调函数
p.then((value)=>{
//promise的状态是fulfilled时,执行
console.log('成功时调用',value);//value是resolve传入的参数
},
()=>{
//promise的状态是reject时,执行
console.log('失败时调用');
})
then方法返回值是一个新的Promise实例,状态是pending
promise的状态不改变,不会执行then里的方法
这意味着我们可以进行链式编码
new Promise((resolve,reject)=>{}).then().then().then()
如何修改上一个then返回的promise的状态
使用return 可以将返回的promise的状态修改成fulfilled
const p = new Promise((resolve,reject)=>{
resolve()
}) //第一个promise
const t = p.then((value)=>{ //返回的t是第二个promise
console.log("成功");
return 123 //调用return 修改t promise的状态
},()=>{
console.log("失败");
})
t.then((value)=>{ //怎么修改t的状态 好让第三个then输出成功2执行↑
console.log('成功2',value);
},()=>{
console.log("失败");
})
什么情况会修改promise为reject呢
如果then方法中,出现代码错误,会将返回的promise实例的状态改为fulfilled
const p = new Promise((resolve,reject)=>{
resolve()
}) //第一个promise
const t = p.then((value)=>{ //返回的t是第二个promise
//如果这里代码出错,会将t实例的状态改成rejected
console.log(a)
},()=>{
console.log("失败");
})
t.then((value)=>{ //怎么修改t的状态 好让第三个then输出成功2执行↑
console.log('成功2',value);
},()=>{
console.log("失败"); //上面出错会执行这一步
})
当promise状态为rejected或promise执行体中出现代码出错时,将执行catch方法,参数是错误信息
const p = new Promise((resolve,reject)=>{
//thorw new Error('error')
// console.log(a);
reject();
}).catch((e)=>{
console.log('失败',e);
})
将多个Promise实例包装成一个新的Promise实例的方法,Promise.all的入参不一定非要是数组,只要是具有Iterator结构的数据集合都行。
Promise.all 等待所有成功返回成功(有一个失败,则进入失败方法)。
应用场景:
var p1 = Promise.resolve(3);
var p2 = 1337;
var p3 = new Promise((resolve, reject) => {
setTimeout(resolve, 100, 'foo');
});
Promise.all([p1, p2, p3]).then(values => {
console.log(values); // [3, 1337, "foo"]
});
/*
实现思路:
1.遍历数组
2.判断是否是promise对象
- 是就执行.then
- 成功就将值添加到返回值数组
- 失败就调用 当前promise的reject()
- 不是 就直接把值添加到返回值数组
3.因为有异步任务 每次添加值都记录一遍index++ 与 数组值相等说明任务
都执行完了 可以执行resolve(结果)
*/
Promise.myall = function (array) {
let result = [];
let index = 0;
return new Promise((resolve, reject) => {
function addData(key, value) {
result[key] = value
index++
//3. 任务都执行完毕 resolve(result)
if (index === array.length) {
resolve(result)
}
}
//1.遍历数组
for (let i = 0; i < array.length; i++) {
let current = array[i];
//2.判断是不是Promise
if (current instanceof Promise) {
//promise对象 执行then
current.then(value => addData(i, value), reason => reject(reason))
} else {
//普通值 添加到返回值数组
addData(i, array[i])
}
}
})
}
4)Promise存在的问****题
new Promise((resolve,reject)=>{
}).then(value=>{
console.log("成功",value);
}).catch(reason=>{
console.log(reason);
})
new Promise((resolve, reject) => {
setTimeout(() => {
resolve("第一次请求数据")
}, 3000)
}).then((data) => {
console.log(data);
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve("第二次请求数据")
},1000)
})
}).then(data=>{
console.log();
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve("第三次请求数据")
},2000)
})
}).then(data=>{
console.log(data);
})
使用async,await语法糖可以让异步代码看起来像同步代码,提高代码可阅读性。
示例:
//原来Promise的写法
function getData() {
fetch("https://jsonplaceholder.typicode.com/posts/1").then(res => {
return res.json()
}).then(res => {
console.log(res);
}).catch(error=>{
console.log(error);
}).finally(()=>{
// 清理工作
})
}
//使用async语法糖
//async将函数标记为异步函数
async function getData(){
// 请求不需要then处理,而是用await
const response = await fetch("https://jsonplaceholder.typicode.com/posts/1")
const json = await response.json()
console.log(json);
}
getData()
使用async,await应该避免的坑:
async function getData(){
const a = await fetch("https://...")
const b = await fetch("https://...")
}
这里的两个fetch并不是并行操作,而是a先获取到,再执行b的请求。更高效的做法是将所有Promise用Promise.all组合起来,然后再去await。
async function getData(){
const promiseA = fetch("https://...")
const promiseB = fetch("https://...")
const[a,b] = await Promise.all([promiseA,promiseB])
}
如果我们希望等待循环中的异步操作都一一完成之后才继续执行,我们应该使用for循环
async function f(){
for (let i of [1,2,3]){
await someAsyncOperation()
}
console.log("done")
}
f()
更进一步,如果我们需要所有请求并发执行,我们可以使用for await
// 并发
async function f(){
const promises = [
someAsyncOperation(),
someAsyncOperation(),
someAsyncOperation(),
]
for await (let result of promises){
//..
}
console.log("done")
}
f()
//async function f(){
// await someAsyncOperation()
//}
//f()
(async()=>{
await someAsyncOperation()
})()