由于前文提过的回调函数的缺点,因此出现promise语法
promise默认的两个词是resolve和reject,而且由于链式操作,也不会出现回调地狱,也可以在then方法中定于对于错误的处理方法。
let _promise = new Promise((resolve, reject) => {resolve("成功");// reject("失败");});console.log(_promise);
promise的状态
传入构造函数中的是executor’执行者’,意为控制promise的状态,如上是成功状态。
promise有三个状态
- pending: 初始状态,既不是成功,也不是失败状态。
- fulfilled: 意味着操作成功完成。
- rejected: 意味着操作失败。
executor中的函数会改变promise的状态。
注意:promise的状态是单向的、不可改变的,如果同时又两个状态,比如resolve和reject,只会按照第一个函数来,后面的不会改变状态。
不同promise状态下的任务
使用then()方法来分别控制不同状态下的执行任务。resolve和reject会调用then中不同状态下的方法
注意then中的方法可以传入reject里面的参数,如下
let _promise = new Promise((resolve, reject) => {// resolve("成功");reject("失败");});_promise.then(() => {console.log("成功啦");},(data) => {console.log("失败啦"+data);});console.log(_promise);
同步任务、宏任务(异步任务)与微任务
在这里先提几个概念。
之前说到异步时提到异步任务的任务队列与主进程是不同的,而异步任务的这个任务队列被称为宏任务。
那么现在我们一共有三个不同的任务进程了。分别是同步任务、宏任务、微任务,那么他们的执行顺序究竟是怎么样的呢。
让我们来看看下面的代码
setTimeout(() => {console.log("宏任务");}, 0);let _promise = new Promise((resolve, reject) => {resolve();console.log("chenggong");});_promise.then(() => {console.log("成功啦");},() => {console.log("失败啦");});console.log(_promise);
上面的代码输出的结果是
可以看到,executor中的代码是作为同步任务进行的,而且执行顺序是同步任务>微任务>宏任务。
promise的工作原理
promise在构造函数执行的时候,就会同步执行executor中的代码,并改变promise的状态。
在改变状态的同时,如果是成功状态,则会安排then中的成功方法进入微任务队列,相应的,失败状态则会安排失败方法进入微任务队列。
这就解释了上面的代码的执行顺序,首先进行同步任务的执行,输出executor中的chenggong和_promise变量,然后进行微任务,输出成功状态的’成功啦’,最后输出宏任务中的’宏任务’。
看个例子
let _promise = new Promise((resolve, reject) => {setTimeout(() => {resolve();console.log("chenggong");}, 0);});_promise.then(() => {console.log("成功啦");},() => {console.log("失败啦");});console.log(_promise);
这个例子将executor中的resolve函数放入了异步函数中,这样resolve就不会在同步线程中执行,因此也不会安排then中的函数进入微任务队列,因此输出结果如图。
不过值得注意的是,虽然promise的状态是pending,但是promisestatus却是resolved,让我费解。
promise.reject
promise.reject返回一个拒绝状态的promise,下例是通过创建一个error实例来传递error信息。
Promise.reject(new Error('fail')).then(() =>{}, (error) =>{console.log(error);})
promise.all
promise.all(iterable) 传入的参数为可迭代对象,会返回一个数组(返回的promise的状态的结果),包含所有参数(包括promise或者是非promise)的完成状态的值(失败或者成功),有以下几种情况
1.传入空,promise会返回已完成状态的promise对象
2.传入的参数中没有promise,会返回异步完成状态的promise
3.其他情况会返回一个pending状态的promise,这个promise会在参数全部状态确定时,返回成功或者失败的值,顺序为原来的参数的顺序,注意只要有一个失败即返回失败状态的promise
成功的时候数组中是全部的成功的值,而失败的时候,返回的是最先失败的结果
promise.race
promise.race(iterable) race为比赛,意思就是传入的可遍历的参数中的promise进行速度上的比赛,只要有一个已经完成,那么就会调用后续对应的then中的方法。
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.allSettled
与promise.all相似,promise.allSettled接收一组promise实例
只有等到所有的promise的实例都结束返回状态,promise.allSettled才会返回fullfilled,而且只会变成fullfilled
如果传入的不是promise实例,则会调用promise.resolve将其转化成实例
promise.allSettled会返回一个数组,数组的成员依原promise的状态而决定,成功的promise会返回status为fullfilled,并包含value的对象,失败的promise会返回status为rejected,并包含失败原因reason的对象。
promise.allSettled可以用于确定所有所有的promise都已经结束
promise.finally
写在promise链条的最后一环,意思是不论promise的结果为什么,都会执行finally里面的代码
finally不接受任何参数,应该是一段不依赖promise状态的代码
promise.catch
一般来说promise可以使用第二个回调函数来处理reject,不过也可以在promise的后面跟上catch来处理错误(这种更好)
因为then的第二个参数只能处理前一轮的promise,而catch可以监听之前所有的,而且更易阅读
与try、catch不同的是,catch方法必须指定处理错误的回调函数,否则不会影响外部的代码运行
catch后也可以跟then方法,也可以跟catch方法继续捕获前一个catch的错误
promise的链式操作
promise的then方法会返回一个新的promise的对象,这个对象的状态由上一个then的方法所决定,因此可以进行链式操作。链式操作不仅会向后传递状态,也会传递值。
async关键字
async关键字其实是promise的语法糖,用来将function转化成一个异步函数,并返回promise对象。
如下,因此也可以使用then,catch等链式操作。
await关键字
await函数只在异步函数内部起作用
async和await关键字可以使用在类或者对象的方法上。
语法
[返回值] = await promise表达式;
这句话的意思是,等待promise表达式运行,如果成功,则会返回promise表达式中resolve回调函数的参数。如果失败,则会抛出异常,异常的值为reject的回调函数的参数,因此可以使用try catch语法。
async function f() {try {var a = await Promise.reject(30);} catch (error) {console.log(errot); // 30}}f();
在promise完成后,会继续后续的代码。await关键字允许其他任务的继续运行,但是异步函数中的后续代码将会被阻塞。
解决方案
如果同时有几个await关键字,会阻塞后续的代码大量时间,但是将promise储存在变量中,await将同时启动他们
async function timeTest() {await timeoutPromise(3000);await timeoutPromise(3000);await timeoutPromise(3000);}async function timeTest() {const timeoutPromise1 = timeoutPromise(3000);const timeoutPromise2 = timeoutPromise(3000);const timeoutPromise3 = timeoutPromise(3000);await timeoutPromise1;await timeoutPromise2;await timeoutPromise3;}
如上,第一种方案需要等待9s,第二种方案只需要3s。
