前言
在Web前端开发中,处理异步操作是非常常见的需求。为了解决这个问题,ES6引入了Promise,ES7新增了async await。
一、promise是干嘛的?
Promise是一个表示异步操作最终完成或失败的对象。它可以将回调地狱转化为链式调用,使代码更加整洁和可读。
Promise 有三种状态:
Promise 状态的转变是不可逆且只能发生一次。
也就是说,一个 Promise 不能从 fulfiled 状态变回 pending 状态,也不能从 rejected 状态变为 pending 或者 fulfiled 状态。
一旦 Promise 从 pending 状态变为 fulfiled 或 rejected ,它就永远不会再改变。
promise是用来解决两个问题的:
简单一句话就是promise是用来解决异步的。
js的执行顺序就是从上到下,先同步后异步。
console.log('1') setTimeout(()=>{ console.log('2') }) console.log('3') //打印结果顺序是132
因为setTimeout是异步,所以会打印3后才打印2,如果我们想打印123,怎么办呢?那就把setTimeout变成同步就可以了,这时候就可以用到promise了。
二、Promise 是同步还是异步?
三、Promise的应用场景
Promise 的构造函数接收一个参数:函数,并且这个函数需要传入两个参数:
1、 then 的用法
then() 方法返回一个 新的Promise对象,then 方法最多传入两个参数:
let res = new Promise((resolve, reject) => { if (true) { resolve("success"); } else { reject("fill"); } }); res.then((data) => { console.log(data); // 处理resolve回调 }).catch((data) => { console.log(data); // 处理reject回调 });
then的链式调用:
从表面上看,Promise只是能够简化层层回调的写法,而实质上,Promise的精髓是“状态”,用维护状态、传递状态的方式来使得回调函数能够及时调用,它比传递callback函数要简单、灵活的多。所以使用Promise的正确场景是这样的:
request('test1.py', data) .then(function (data1) { console.log('第一次请求成功, 这是返回的数据:', data1); return request('test2.py', data1); }) .then(function (data2) { console.log('第二次请求成功, 这是返回的数据:', data2); return request('test3.py', data2); }) .then(function (data3) { console.log('第三次请求成功, 这是返回的数据:', data3); }) .catch(function (error) { console.log('sorry, 请求失败了, 这是失败信息:', error); });
Promise对象的then方法返回一个新的Promise对象,因此可以通过链式调用then方法。
then方法接收两个函数作为参数,第一个参数是Promise执行成功时的回调,第二个参数是Promise执行失败时的回调。两个函数只会有一个被调用,函数的返回值将被用作创建then返回的Promise对象。
then的两个参数的返回值可以是以下三种情况中的一种:
var res = new Promise(function (resolve, reject) { resolve(1); }); res.then(function (value) { console.log(value); // 1 return value * 2; // return一个同步的值(resolved状态) }).then(function (value) { console.log(value); // 2 }).then(function (value) { console.log(value); // 上面的then没有return,因此默认是return undefined return Promise.resolve(3); }).then(function (value) { console.log(value); // 接收到resolve(3) return Promise.reject(4); }).then(function (value) { console.log('resolve: ' + value); }, function (err) { console.log('reject: ' + err); // 接收到reject(4) }) // 1 // 2 // undefined // 3 // "reject: 4"
2、catch 的用法
用于处理Promise失败后的回调函数,也返回一个新的Promise对象。
它主要用于捕获异步操作过程中发生的错误。其实它和 then 的第二个参数一样,用来指定 reject 的回调。 用法是这样:
let res = new Promise((resolve, reject) => { if (true) { resolve("success"); } else { reject("fill"); } }); p.then((data) => { console.log('resolve:' + data); }) .catch((err) => { console.log('reject:' + err); })
效果和写在then的第二个参数里面一样。
不过它还有另外一个作用:在执行 resolve 的回调(也就是上面 then 中的第一个参数)时,如果抛出异常了(代码出错了),那么并不会报错卡死 js,而是会进到这个 catch 方法中。
catch 既能处理 reject 回调,也能捕捉错误。
3、finally 的用法
finally()方法用于指定不管 Promise 对象最后状态如何,都会执行的操作,它常常用于清理资源或执行一些不论成功失败都需要进行的操作。
promise .then(res => {···}) .catch(error => {···}) .finally(() => {···});
四、Promise 的静态方法
1、Promise.all 的用法
这个方法接受一个Promise对象的数组作为参数,只有当数组中的所有Promise对象都成功完成时,它才会返回一个新的成功的Promise对象,其结果是一个由每个Promise对象的返回值组成的数组,如果有任何一个Promise对象失败,all()方法将立即返回一个失败的Promise对象。
let p1 = new Promise(function(resolve, reject){}) let p2 = new Promise(function(resolve, reject){}) let p3 = new Promise(function(resolve, reject){}) let p = Promise.all([p1, p2, p3]) p.then(() => { // 三个都成功,则成功 }, function(){ // 只要有任何一个失败,则失败 })
Promse.all 在处理多个异步处理时非常有用,比如说一个页面上需要等两个或多个 ajax 的数据回来以后才正常显示。
需要特别注意的是,Promise.all获得的成功结果的数组里面的数据顺序和Promise.all接收到的数组顺序是一致的。
2、Promise.race 的用法
Promise.race([p1, p2, p3])接受一个Promise对象的数组作为参数,哪个结果获得的快,就返回那个结果,不管结果本身是成功状态还是失败状态。
与all()不同的是,race()方法会在数组中的任何一个Promise对象最先完成(无论是成功还是失败)时,就返回一个新的Promise对象,其结果就是那个最先完成的Promise对象的结果。其它的promise实例仍然会继续运行,只不过其状态和结果不会归于最终的结果。
let p1 = new Promise((resolve, reject) => { setTimeout(() => { resolve('success') },1000) }) let p2 = new Promise((resolve, reject) => { setTimeout(() => { reject('failed') }, 500) }) Promise.race([p1, p2]).then((result) => { console.log(result) }).catch((error) => { console.log(error) // 打开的是 'failed' })
Promise.race()常用于前端发起某个异步请求获取数据,要求在规定时间内如果没拿到数据则执行另外相应的操作。
3、Promise.allSettled 的用法
和 Promise.all() 相似,接收一个 Promise 对象数组作为参数。
Promise.allSettled()不会因为数组中的某个 Promise 被拒绝而立即返回,它会等待所有 Promise 都达到稳定(Settled)状态(即无论是 fulfilled 还是 rejected )后才会完成,返回一个包含每个 Promise 结果的数组。
const p1 = Promise.resolve(3); const p2 = 42; // P2 是一个非 Promise 值,它会被隐式地转换为一个已解析的 Promise。 const p3 = new Promise((resolve, reject) => { setTimeout(reject, 100, 'foo'); }); const p4 = new Promise((resolve, reject) => { setTimeout(resolve, 50, 'bar'); }); const promises = [p1, p2, p3, p4]; Promise.allSettled(promises). then((results) => results.forEach((result) => console.log(result.status))); // 输出: // "fulfilled" // "fulfilled" // "rejected" // "fulfilled"
Promise.allSettled() 允许你观察一组异步操作的所有结果,无论成功与否,这对于获取并处理所有任务的最终状态非常有用。
Promise.all() 则更关注所有 Promise 是否都成功完成,它适用于需要所有任务成功完成才能继续下一步场景。
4、Promise.resolve 的用法
Promise.resolve(…) 可以接收一个 值 或者是一个 Promise对象 作为参数。
var p1 = Promise.resolve(1); var p2 = Promise.resolve(p1); var p3 = new Promise(function(resolve, reject){ resolve(1); }); var p4 = new Promise(function(resolve, reject){ resolve(p1); }); console.log(p1 === p2); console.log(p1 === p3); console.log(p1 === p4); console.log(p3 === p4); p4.then(function(value){ console.log('p4=' + value); }); p2.then(function(value){ console.log('p2=' + value); }) p1.then(function(value){ console.log('p1=' + value); }) // true // false // false // false // p2=1 // p1=1 // p4=1
当参数是普通值时,它返回一个resolved状态的Promise对象,对象的值就是这个参数;
当参数是一个Promise对象时,它直接返回这个Promise参数。因此,p1 === p2。
但通过new的方式创建的Promise对象都是一个新的对象,因此后面的三个比较结果都是false。
另外,为什么p4的then最先调用,但在控制台上是最后输出结果的呢?因为p4的resolve中接收的参数是一个Promise对象p1,resolve会对p1”拆箱“,获取p1的状态和值,但这个过程是异步的。
5、Promise.reject 的用法
用于创建一个已拒绝的 Promise,接收一个原因(reason)作为参数,并返回一个已拒绝为该原因的 Promise 对象。
const promise = Promise.reject('error'); promise.catch((reason) => { console.log(reason); // 输出 "error" });
五、async await的作用及应用场景
async函数返回一个Promise对象,可以通过await关键字来暂停函数的执行,等待Promise对象的状态变为resolved后继续执行,可以更简洁地处理异步操作。
async await这两个命令是成对出现的,如果使用await没有在函数中使用async命令,那就会报错,如果直接使用async没有使用await不会报错,只是返回的函数是个promise,可以,但是没有意义,所以这两个一起使用才会发挥出它们本身重要的作用。
function getData() { //异步 return new Promise((resolve, reject) => { setTimeout(() => { resolve("结果"); }); }); } async function test() { const resultData = await getData(); console.log(resultData); } test();
相较于Promise,async await有何优势?