JavaScript的Promise和异步函数 async-await

概述

promise是js的异步请求机制,再ES6时代引入官方的js文档,解决之前的回调函数引起的回调地狱。
promise可以分为两部分去理解 一个是promise构造函数本身,二是promise的一系列方法。

promise构造函数

首先要明确的一点是,promise本身是一个构造函数,即使用 new Promise 的方法生成一个对象,且生成实例时 需要传入一个执行器函数,通常情况如下所示:

new Promise( (resolve, reject) => {
	if( statement){
		resolve();
	} else {
		reject();
	}
})

我们先从promise构造函数所产生的对象本身开始:

在这里插入图片描述
这是输出的一个promise 对象 ,有两个属性:[[PromiseState]][[PromiseResult]]

[[PromiseState]] 一共有三个值:“pending”、 “fulfilled”、“rejected”,再中文语境下分别代表 未处理,成功,失败。

该属性默认状态为 pending,状态只可 改变一次

若新创建一个promise实例,不加任何改变,该属性的输出为 pending
在执行器函数内 使用 rejected或 resolve 更改状态 该属性的输出为 rejectedfulfilled
但在执行一次rejected或 resolve后,再次执行rejected或 resolve,结果不会改变:

let result = new Promise((resolve,reject) => {
    })
console.log(result); 
// [[PromiseState]]: "pending"

//使用rejecte()输出 "rejected"
let result = new Promise((resolve,reject) => {
    	resolve()
})
console.log(result); 
// [[PromiseState]]: "fulfilled""

//两次执行 状态只改变一次
let result = new Promise((resolve,reject) => {
	reject()
	resolve()//不会改变状态
})
console.log(result);
// [[PromiseState]]: "rejected"

而第二个属性 [[PromiseResult]] 为调用改变状态函数的值,例:

let result = new Promise((resolve,reject) => {
        resolve(1);
    })
console.log(result);

/*输出结果如下:
[[Prototype]]: Promise
[[PromiseState]]: "fulfilled"
[[PromiseResult]]: 1*/

所以我们可以简单的使用一下prmoise

let i = 2; 
let result = new Promise((resolve,reject) => {
    if(i >1){
        resolve(i)
    } else {
        reject(i)
    }
})
console.log(result);
/*
[[Prototype]]: Promise
[[PromiseState]]: "fulfilled"
[[PromiseResult]]: 2
*/

let i = 0; 
let result = new Promise((resolve,reject) => {
     if(i >1){
         resolve(i)
     } else {
         reject(i)
     }
 })
 console.log(result);
 /*
[[Prototype]]: Promise
[[PromiseState]]: "rejected"
[[PromiseResult]]: 0
*/

总结promise对象本身

使用promise构造函数 最终生成一个包含两个属性的promise对象,该对象的PromiseState属性的值只能更改一次。虽然说 promise是解决异步问题的,但目前为止,promise一直都是同步执行。

promise原型对象上的方法

promise.prototype上一共有三个方法 then,catch,finally

then方法

then方法可以说是最重要的方法,then中的方法是 异步执行 的。它支持传入两个函数,第一个函数处理promise 对象为 fullfilled 状态,第二个函数处理 promise 对象 为rejected状态。

不一定两个函数都要传入,可以用null代替第一个函数,也可以不写第二个函数。但这样没有意义,因为promise的作用就是异步处理。

let i = 2; 
    let result = new Promise((resolve,reject) => {
        if(i >1){
            resolve(i)
        } else {
            reject(i)
        }
    })
result.then( 
   () => {console.log('resolve')},
   ()=> {console.log('reject')}
)
console.log(result);
 /*Promise {<fulfilled>: 2}
	test.html:81 resolve*/

then方法的执行时机

then中代码是在微任务中执行,微任务在宏任务执行完之后执行(宏任务即主要的js代码)微任务执行后再执行消息列队中的代码,回调函数,计时器,dom监听都会进入消息列队。

请添加图片描述

再看一道例题:

//请写出输出顺序
const first = () => (new Promise((resolve, reject) => {
    console.log(3);
    let p = new Promise((resolve, reject) => {
        console.log(7);
        setTimeout(() => {
            console.log(5);
            resolve(6);
        }, 0)
        resolve(1);
    });
    resolve(2);
    p.then((arg) => {
        console.log(arg);
    });

}));

first().then((arg) => {
    console.log(arg);
});
console.log(4);

答案如下:

// => 3
// => 7
// => 4
// => 1
// => 2
// => 5

为什么不是374562呢?
到目前未知,我们已经知道promise构造函数中的代码是同步执行,而then中的代码是异步执行。
那么可以确定前三个输出的值为 374 而当代码执行到 setTimeout(()... 时,会将整体放入到消息列队中,随后执行 resolve(1) 更改promise的状态和值,当执行完 resolve(2); 遇到then方法,将方法放入微任务中 继续往后执行。
宏任务执行完成之后(执行完 console.log(4) 之后)开始执行微任务,也就是首先放入的then方法

 p.then((arg) => {
        console.log(arg);
    });

因为这时p的状态已经被 resolve(1); 所更改 所以输出 1
再执行 first().then((arg) 输出2。当两个then所在的微任务执行完成之后 再执行 消息队列中的 settimeout 函数 输出7 而p的状态已经改变过 由promise的规则 状态只能改变一次 所以 resolve(6) 无效。

then方法的返回值以及链式调用

promise使用链式调用的方法,将多个层级的回调地狱 问题 转化为扁平化的代码:

new Promise( resolve => resolve(100))
.then(then1 => 10)
.then(then2 => console.log(then2))
//10

之所以可以这样做,是因为then也返回一个promise 对象,只要then内部的代码不出错,新的pormise状态就为fullfilled,且return 出去的值就是 新的promise 的值。
若then中的代码出错,则返回一个rejected。

catch方法

在then的开头有提到过接收两个函数,第一个处理fullfilled状态,第二个处理rejected状态。而catch方法是一个语法糖,一个catch就相当于
Promise.prototype.then(null,onRejected),将then的方法改为下状态:

let i = 2; 
    let result = new Promise((resolve,reject) => {
        if(i >1){
            resolve(i)
        } else {
            reject(i)
        }
    })
result.then( 
   () => {console.log('resolve')})
   .catch( consoole.log('rejected'))
console.log(result);
 /*Promise {<fulfilled>: 2}
	test.html:81 resolve*/

catch的优点在于当存在多个链式调用时,只需在最后写一个catch即可处理所有的错误情况。

new Promise( resolve => resolve(100))
.then(then1 => ok)
.then(then2 => console.log(then2))
.catch(error => console.log(error))
//ReferenceError: ok is not defined
//at test.html:86:20

finally方法

无论promise是rejected或者fullfilled 都会执行finally方法,且无论then和catch,是否执行 也会执行finally方法。虽然用的次数不如then和catch多,但在清除代码时用finally有奇效。

Promise的静态方法

promise并非绝对需要在待定状态下,通过执行器函数改变状态。
也可以通过Promise.resolve() 或者Promise.reject() 来改变状态。

Promise.resolve(1).then(then => {console.log(then)})
//1

除上述这些,promise还有一些别的用法,但主要和常用内容就这三点,其余可以参考MDN,或者JS红宝书。

异步函数

期约 Promise 是在ES6提出的,而异步函数则到了ES8。异步函数的底层依然是期约,相当于一个语法糖,但也有一些异同。

基本用法

async为带有异步特征的函数,async的结果是一个promise对象,若async有返回值(有return 语句),该值则为 promise resolve的值。

async function foo(){
 return 1
}
foo().then( then => {console.log(then)})
//1

async函数本身是同步执行的,真正实现异步操作的是 async 内使用 await 关键字修饰的函数。即await 后面的内容都是异步执行。

    function func(){
        console.log(2345)
        return 1
    }
    async function foo(){
        let result = await func()  
        console.log(6789)
        console.log(result)
        return 10
    }
    console.log(foo())
    /*注意输出的顺序
    2345
   Promise {<pending>}  //实际上是resolve,但输出是promise为待定
	[[Prototype]]: Promise
	[[PromiseState]]: "fulfilled"
	[[PromiseResult]]: 10
     6789
     1 */

这里再定义了两个函数后,并没有执行语句,一直到最后一行 console.log(foo()) 开始执行foo()函数。

foo函数第一行就是异步操作,后续所有内容都放到微任务中,此时宏任务中没有其他代码,开始执行 func()函数,控制台输出 2345 之后给result赋值为 1。

当 await函数执行完成之后 async 就已经生成一个 Promise 对象 但因为没有返回值 所以是 pending状态。

在这里插入图片描述

上图是在await所在行 插入断点后的输出状态。

随后执行后两行 输出 6789 以及result的值。并通过return 将 async的 promise对象 状态改为 fullfilled,值为 10

异步函数的错误处理

引入async和await之后 函数本身就变为同步执行 因此 catch的方法就不再适用,否则会报错。而在遇到错误时,就要用 同步函数的错误处理模块 try()....catch()...

console.log(foo())
 function func(){
      console.log(ok) //并没有ok这个变量
      return 1
  }
  async function foo(){
      try {
          let result = await func()
          console.log(result)
          console.log(5679)
      } catch (error) {
          console.log(error)
          console.log(2345)
      }
      return 10
  }
 /*ReferenceError: ok is not defined
   	at func (test.html:87:21)
	at foo (test.html:92:32)
   	at test.html:85:17
2345
Promise {<fulfilled>: 10}
	[[Prototype]]: Promise
	[[PromiseState]]: "fulfilled"
	[[PromiseResult]]: 10*/

可见,当await 所修饰的函数出先错误时,将直接执行 catch 中的内容,不再执行func() 中的内容。
上述代码也可以通过 throw 的方法 改为 rejected

    console.log(foo())
    function func(){
        console.log(ok)
        return 1
    }
    async function foo(){
        try {
            let result = await func()
            console.log(6789)
            console.log(result)
            
        } catch (error) {
            throw error
            console.log(2345)
        }
        return 10
    }
    foo().catch(error => {console.log(error)})
    /*
	Promise {<rejected>: ReferenceError: ok is not defined
    at func 
    	[[Prototype]]: Promise
    	[[PromiseState]]: "rejected"
    	[[PromiseResult]]: ReferenceError: ok is not defined
	ReferenceError: ok is not defined
    	at func (test.html:87:21)
    	at foo (test.html:92:32)
    	at test.html:103:5 
    Uncaught (in promise) ReferenceError: ok is not defined
    	at func (test.html:87:21)
    	at foo (test.html:92:32)
   	 	at test.html:102:17

此时async的状态为 rejected 。

同Promise一样,异步函数百分之七十以上的功能已经讲完,剩下的以后可以慢慢探索。

——完

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值