目录
1、Promise
Promise 是一个 Es 6 提供的类,目的是更加优雅地书写复杂的异步任务。可以解决嵌套式的回调地域问题,Promise 将嵌套格式的代码变成了顺序格式的代码。
//回调地域
setTimeout(function () {
console.log("红灯");
setTimeout(function () {
console.log("绿灯");
setTimeout(function () {
console.log("黄灯");
}, 1000);
}, 2000);
}, 3000);
//promise.then链式调用解决回调地域问题, Promise 将嵌套格式的代码变成了顺序格式的代码。
new Promise(function (resolve, reject) {
setTimeout(function () {
console.log("red");
resolve();
}, 3000);
})
.then(function () {
return new Promise(function (resolve, reject) {
setTimeout(function () {
console.log("green");
resolve();
}, 2000);
});
})
.then(function () {
setTimeout(function () {
console.log("yellow");
}, 1000);
});
三种状态:pending 、fulfilled、rejected
Promise 对象代表一个异步操作,有三种状态 :pending 等待、fulfilled完成/成功、rejected拒绝/失败。只有异步操作的结果可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。
promiise状态变化不可逆
等待->成功:Promise对象的状态可以从pending变为fulfilled,并携带了成功的结果value。通过resolve触发,fulfilled
状态会触发后续的then
回调
等待->失败:也可以从pending变为rejected。携带失败的理由reason。通过reject触发,rejected
状态会触发后续的catch
回调
Promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolve和reject。它们是两个函数,由JavaScript引擎提供。
resolve函数的作用是,将Promise对象的状态从pending变为Resolved(或fulfilled),在异步操作成功时调用,并将异步操作的结果value作为参数传递出去。
reject函数的作用是,将Promise对象的状态从Pending变为Rejected,在异步操作失败时调用,并将异步操作报出的错误作为参数传递出去
使用 then 方法处理 Promise 成功状态的回调函数
使用 catch 方法处理 Promise 失败状态的回调函数
let promise = new Promise((resolve, reject) => {
// 接收一个callback。参数是成功函数与失败函数
setTimeout(() => {
let num = parseInt(Math.random() * 100);
console.log("num=", num);
// 如果数字大于50就调用成功的函数,并且将状态变成Resolved
if (num > 50) {
resolve("成功");
} else {
// 否则就调用失败的函数,将状态变成Rejected
reject("失败");
}
}, 1000);
});
//使用 then 方法处理 Promise 成功状态的回调函数,使用 catch 方法处理 Promise 失败状态的回调函数
promise
.then((res) => {
console.log(res);
})
.catch((err) => {
console.log(err);
});
then 方法可以接收两个回调函数作为参数,第一个回调函数是Promise对象的状态改变为 resoved 是调用,第二个回调函数是 Promise 对象的状态变为 rejected 时调用。其中第二个参数可以省略。
catch 方法,该方法相当于 最近的then 方法的第二个参数,指向 reject 的回调函数,另一个作用是,在执行resolve回调函数时,如果出错,抛出异常,进入catch 方法中。
finally 方法是在 Promise 执行的最后一定会执行的序列
注意:
then、catch返回的promise是新的promise,不是原来的promise。
promise对象的错误会“冒泡”,直到被捕获为止,错误会被下一个catch语句捕获。
catch 只捕获最近的 then 的回调函数,前面的then的执行不成功的结果,有后面 then 的 reject 回调函数执行,如果没有后续 then 回调函数执行,则会被 catch 捕获执行;
then和catch对状态的影响
then
正常返回fulfilled
,里面有报错返回rejected
catch
正常返回fulfilled
,里面有报错返回rejected
const p1 = Promise.resolve().then(()=>{
return 100
})
console.log('p1', p1) // fulfilled会触发后续then回调
p1.then(()=>{
console.log(123)
}) // 打印123
const p2 = Promise.resolve().then(()=>{
throw new Error('then error')
})
// p2是rejected会触发后续catch回调
p2.then(()=>{
console.log(456)
}).catch(err=>{
console.log(789)
})
// 打印789
const p1 = Promise.reject("my error").catch(() => {
console.log("catch error");
});
p1.then(() => {
console.log(1); //打印1
});
const p2 = Promise.reject("my error").catch(() => {
throw new Error("catch error");
});
p2.then(() => {
console.log(2);
}).catch(() => {
console.log(3); //打印3
});
// 第一题
Promise.resolve()
.then(()=>console.log(1))// 状态返回fulfilled
.catch(()=>console.log(2)) // catch中没有报错,状态返回fulfilled,后面的then会执行
.then(()=>console.log(3)) // 1,3
// 整个执行完没有报错,状态返回fulfilled
// 第二题
Promise.resolve()
.then(()=>{ // then中有报错 状态返回rejected,后面的catch会执行
console.log(1)
throw new Error('error')
})
.catch(()=>console.log(2)) // catch中没有报错,状态返回fulfilled,后面的then会执行
.then(()=>console.log(3)) // 1,2,3
// 整个执行完没有报错,状态返回fulfilled
// 第三题
Promise.resolve()
.then(()=>{//then中有报错 状态返回rejected,后面的catch会执行
console.log(1)
throw new Error('error')
})
.catch(()=>console.log(2)) // catch中没有报错,状态返回fulfilled,后面的catch不会执行
.catch(()=>console.log(3)) // 1,2
// 整个执行完没有报错,状态返回fulfilled
promise.all
promise.all可以将多个promise实例包装成一个新的promise实例。同时,成功和失败的返回值是不同的,
成功的时候返回一个数组,失败时则返回最先被reject失败的状态的值。
所有的请求都完成时,才返回一个数组
只要有一个失败了,就先返回这个失败的请求
let p1 = new Promise((resolve,reject) => {
resolve('成功了')
})
let p2 = new Promise((resolve,reject) => {
resolve('success')
})
let p3 = new Promise((resolve,reject) => {
reject('失败了')
})
Promise.all([p1, p2]).then(res => {
console.log(res) //[ '成功了', 'success' ]
}).catch(err => {
console.log(err)
})
Promise.all([p1, p2, p3]).then(res => {
console.log(res)
}).catch(err => {
console.log(err) // 失败了
})
promise.race
顾名思义,promise.race就是赛跑的意思,就是说promise.race([p1, p2, p3])里哪个结果捕获的快,就返回哪个结果
不管成功还是失败
let p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("成功了");
}, 1000);
});
let p2 = new Promise((resolve, reject) => {
setTimeout(() => {
reject("失败了");
}, 500);
});
Promise.race([p1, p2])
.then((res) => {
console.log(res);
})
.catch((err) => {
console.log(err);
});
Promise 优点
Promise 对象可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数;
Promise 对象提供统一的接口,使得控制异步操作更加容易。
Promise 缺点
无法取消 Promise,一旦新建它就会立即执行,无法中途取消;
如果不设置回调函数,Promise 内部抛出的错误,不会反应到外部;
当处于 Pending 状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)
2、async/await
async/await是es7出的一种解决异步的一种方案, 背后原理就是promise。
async
封装Promise
await
处理Promise
成功try...catch
处理Promise
失败
async:当一个函数前面加上async,那么他就成了一个异步函数。async函数会返回一个promise对象。可以使用 then 方法添加回调函数。
async function helloAsync(){
return "helloAsync";
}
console.log(helloAsync()) // Promise {<resolved>: "helloAsync"}
helloAsync().then(v=>{
console.log(v); // helloAsync
})
await:await 操作符用于等待一个 Promise 对象, 它只能在异步函数 async function 内部使用。它相当于promise的then,await是同步写法,但本质还是异步调用。
正常情况下,await 命令后面是一个 Promise 对象,它也可以跟其他值,如字符串,布尔值,数值以及普通函数
await会阻塞后面的代码,等主程序执行完,再回来执行。
async function a() {
console.log("1");
console.log("2");
}
a();
console.log("3");
async function b() {
var c = await 10;
console.log(c);
console.log("one");
console.log("two");
}
b();
console.log("three");
//1
//2
//3
//three
//10
//one
//two
async function async1 () {
console.log('async1 start')
await async2()
console.log('async1 end') // 关键在这一步,它相当于放在 callback 中,最后执行
// 类似于Promise.resolve().then(()=>console.log('async1 end'))
}
async function async2 () {
console.log('async2')
}
console.log('script start')
async1()
console.log('script end')
// 打印
// script start
// async1 start
// async2
// script end
// async1 end
async function async1 () {
console.log('async1 start') // 2
await async2()
// await后面的下面三行都是异步回调callback的内容
console.log('async1 end') // 5 关键在这一步,它相当于放在 callback 中,最后执行
// 类似于Promise.resolve().then(()=>console.log('async1 end'))
await async3()
// await后面的下面1行都是异步回调callback的内容
console.log('async1 end2') // 7
}
async function async2 () {
console.log('async2') // 3
}
async function async3 () {
console.log('async3') // 6
}
console.log('script start') // 1
async1()
console.log('script end') // 4
async function async1 () {
console.log('async1 start')
await async2() // 这一句会同步执行,返回 Promise ,其中的 `console.log('async2')` 也会同步执行
console.log('async1 end') // 上面有 await ,下面就变成了“异步”,类似 cakkback 的功能(微任务)
}
async function async2 () {
console.log('async2')
}
console.log('script start')
setTimeout(function () { // 异步,宏任务
console.log('setTimeout')
}, 0)
async1()
new Promise (function (resolve) { // 返回 Promise 之后,即同步执行完成,then 是异步代码
console.log('promise1') // Promise 的函数体会立刻执行
resolve()
}).then (function () { // 异步,微任务
console.log('promise2')
})
console.log('script end')
// 同步代码执行完之后,屡一下现有的异步未执行的,按照顺序
// 1. async1 函数中 await 后面的内容 —— 微任务(先注册先执行)
// 2. setTimeout —— 宏任务(先注册先执行)
// 3. then —— 微任务
// 同步代码执行完毕(event loop - call stack被清空)
// 执行微任务
// 尝试DOM渲染
// 触发event loop执行宏任务
// 输出
// script start
// async1 start
// async2
// promise1
// script end
// async1 end
// promise2
// setTimeout
3、为什么async/await更好?
-
使用async函数可以让代码简洁很多
-
不需要像Promise一样需要then
-
不需要写匿名函数处理Promise的resolve的值
-
也不需要定义多余的data变量,还避免了嵌套代码