Promise

promise asyan await

1.回调函数的分类

什么是回调:我们定义的,我们没有调用,最终执行了

1.同步的回调函数:
** 理解: 立即在主线程上执行, 不会放入回调队列中。
** **例子: 数组遍历相关的回调函数 **

2.异步的回调函数:
** 理解: 不会立即执行, 会放入回调队列中以后执行
** **例子: 定时器回调 / ajax回调 **

异步回调一般都是需要时间的,像定时器,成功结果的回调和失败结果的回调这种一般都是异步回调

2.控制台遇到的错误以及分析

2.1常见的错误类型

  • Erroe :是所有错误类型的父类

进一步理解JS中的错误(Error)和错误处理
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Error

  • ReferenceError: 引用的变量不存在

    1
    console.log(a);
  • TypeError: 数据类型不正确

    demo() 函数执行后返回的是一个空的 空的在调用就不行了

    1
    2
    const demo = ()=>{}
    demo()()
  • RangeError: 数据值不在其所允许的范围内 –死循环

    1
    const demo = ()=>{demo()}
  • **SyntaxError: 语法错误 **

2.2js错误的处理

try:中放可能存在错误的代码如果有错误进入 catch,catch有一个error的参数里放的是错误信息

1
2
3
4
5
try{
console.log(a);
}catch(error){
console.log(error);
}

throw 抛出错误

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function demo(){
const date = Date.now()
if(date % 2 === 0){
console.log('偶数,可以正常工作');
}else{
//如果是偶数的话就抛出错误 抛出的是一个错误对象Error
throw new Error('奇数,不可以工作!')
}
}
try {
demo()
} catch (error) {
//catch 捕获的也是你所抛出的错误 如果是对象就捕获对象如果是字符串就接受字符串
console.log('@',error);
}
  • 错误对象的属性
    1. message :错误对象的相关信息
    2. stack:记录信息 错误出现在第几行等等

3.promise

简介:

**promise 翻译过来就是许诺 承诺的意思 **

作用是解决异步编程(以前是使用回纯回调函数实现的)

3.1 promise 的基本使用

** 1.Promise不是回调,是一个内置的构造函数,是程序员自己new调用的。**
2.new Promise的时候,要传入一个回调函数,它是同步的回调,会立即在主线程上执行,它被称为executor函数
3.每一个Promise实例都有3种状态:初始化(pending)、成功(fulfilled)、失败(rejected)
4.每一个Promise实例在刚被new出来的那一刻,状态都是初始化(pending)
5.executor函数会接收到2个参数,它们都是函数,分别用形参:resolve、reject接 收

1.调用resolve函数会:
** (1).让Promise实例状态变为成功(fulfilled)
** (2).可以指定成功的value。
2.调用reject函数会:
** (1).让Promise实例状态变为失败(rejected)**
** ** (2).可以指定失败的reason。

1
2
3
4
5
//创建一个Promise实例对象
const p = new Promise((resolve, reject)=>{
reject('ok')
})
console.log('@',p); //一般不把Promise实例做控制台输出
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

1. 重要语法
new Promise(executor)构造函数
Promise.prototype.then方法

2. 基本编码流程
1.创建Promise的实例对象(pending状态), 传入executor函数
2.在executor中启动异步任务(定时器、ajax请求)
3.根据异步任务的结果,做不同处理:
3.1 如果异步任务成功了:我们调用resolve(value), 让Promise实例对象状态变为成功(fulfilled),同时指定成功的value
3.2 如果异步任务失败了:我们调用reject(reason), 让Promise实例对象状态变为失败(rejected),同时指定失败的reason
4.通过then方法为Promise的实例指定成功、失败的回调函数,来获取成功的value、失败的reason
注意:then方法所指定的:成功的回调、失败的回调,都是异步的回调。

3. 关于状态的注意点:
1.三个状态:
pending: 未确定的------初始状态
fulfilled: 成功的------调用resolve()后的状态
rejected: 失败的-------调用reject()后的状态
2.两种状态改变
pending ==> fulfilled
pending ==> rejected
3.状态只能改变一次!!
4.一个promise指定多个成功/失败回调函数, 都会调用吗?

3.2promise 结合ajax

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<script type="text/javascript">
const p=new Promise((resolve,reject)=>{
const xhr=new XMLHttpRequest();
xhr.onreadystatechange=()=>{
if(xhr.readyState===4){
if(xhr.status===200){
resolve(xhr.response);
}else{
reject('请求出错');
}
}
}
xhr.open("GET","https://api.apiopen.top/getJoke");
xhr.responseType='json';
xhr.send();
})
p.then(
(resolev)=>{console.log('成功',resolev)},
(reject)=>{console.log('失败')}
)

</script>

简答的封装一个ajax

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
<script type="text/javascript">

/*
定义一个sendAjax函数,对xhr的get请求进行封装:
1.该函数接收4个参数:url(请求地址)、data(参数对象)、success(成功的回调)、error(失败的回调)
*/
function sendAjax(url,data,success,error){
//实例xhr
const xhr = new XMLHttpRequest()
//绑定监听
xhr.onreadystatechange = ()=>{
if(xhr.readyState === 4){
if(xhr.status >= 200 && xhr.status < 300) success(xhr.response);
else error('请求出了点问题');
}
}
//整理参数
let str = ''
for (let key in data){
str += `${key}=${data[key]}&`
}
str = str.slice(0,-1)
xhr.open('GET',url+'?'+str)
xhr.responseType = 'json'
xhr.send()
}

sendAjax(
'https://api.apiopen.top/getJoke',
{page:1,count:2,type:'video'},
response =>{
console.log('第1次成功了',response);
sendAjax(
'https://api.apiopen.top/getJoke',
{page:1,count:2,type:'video'},
response =>{
console.log('第2次成功了',response);
sendAjax(
'https://api.apiopen.top/getJoke',
{page:1,count:2,type:'video'},
response =>{
console.log('第3次成功了',response);
},
err =>{console.log('第3次失败了',err);}
)
},
err =>{console.log('第2次失败了',err);}
)
},
err =>{console.log('第1次失败了',err);}
)



</script>

3.3promise 常用的api

    1. Promise构造函数: new Promise (executor) {}
      ** executor函数: 是同步执行的,(resolve, reject) => {}
      ** resolve函数: 调用resolve将Promise实例内部状态改为成功(fulfilled)。
      ** reject函数: 调用reject将Promise实例内部状态改为失败(rejected)。
      ** 说明: excutor函数会在Promise内部立即同步调用,异步代码放在excutor函数中。
      1. Promise.prototype.then方法:

      ** Promise实例.then(onFulfilled,onRejected)**
      ** onFulfilled: 成功的回调函数 (value) => {}**
      ** onRejected: 失败的回调函数 (reason) => {}
      ** 特别注意(难点):then方法会返回一个新的Promise实例对象

      1. **Promise.prototype.catch方法: **

      Promise实例.catch(onRejected)
      onRejected: 失败的回调函数 (reason) => {}
      说明: catch方法是then方法的语法糖, 相当于: then(undefined, onRejected)

      1. Promise.resolve方法: Promise.resolve(value)
        说明: 用于快速返回一个状态为fulfilled或rejected的Promise实例对象
        备注:value的值可能是:(1)非Promise值 (2)Promise值

      Promise.reject方法: Promise.reject方法(reason)
      ** 说明: 用于快速返回一个状态必为rejected的Promise实例对象**

如果传入的值是数组 对象 数字这种 那么它生成的就是你指定的成功或者失败的promise

如果传入的是promise实例对象

**** Promise.reject方法(reason) 不管参数是成功的还是失败的返回的结果都是失败的 ****

1
2
3
4
5
6
const p0 = Promise.resolve(100)
const p = Promise.reject(p0)
p.then(
value => {console.log('成功了',value);},
reason => {console.log('失败了',reason);}
)
    1. Promise.all方法: Promise.all(promiseArr)
      promiseArr: 包含n个Promise实例的数组
      说明: 返回一个新的Promise实例, 只有所有的promise都成功才成功, 只要有一个失败了就直接失败。

    只要数组里面有一个失败了就都失败了

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    const p1 = Promise.resolve('a')
    const p2 = new Promise((resolve,reject)=>{
    setTimeout(()=>{
    reject('b')
    },500)
    })
    const p3 = new Promise((resolve,reject)=>{
    setTimeout(()=>{
    reject('c')
    },2000)
    })
    const x = Promise.all([p1,p2,p3])
    x.then(
    value => {console.log('成功了',value);},
    reason => {console.log('失败了',reason);}
    )
    1. Promise.race方法: Promise.race(promiseArr)
      promiseArr: 包含n个Promise实例的数组
      说明: 返回一个新的Promise实例, 成功还是很失败?以最先出结果的promise为准。

    成功还是失败取决于谁最快的结果

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    const p1 = Promise.reject('a')
    const p2 = new Promise((resolve,reject)=>{
    setTimeout(()=>{
    resolve('b')
    },500)
    })
    const p3 = new Promise((resolve,reject)=>{
    setTimeout(()=>{
    reject('c')
    },2000)
    })
    const x = Promise.race([p3,p1,p2])
    x.then(
    value => {console.log('成功了',value);},
    reason => {console.log('失败了',reason);}
    )

3.4 如何改变promise的状态

改变Promise实例的状态和指定回调函数谁先谁后?
1.都有可能, 正常情况下是先指定回调再改变状态, 但也可以先改状态再指定回调
2.如何先改状态再指定回调? 延迟一会再调用then()
3.Promise实例什么时候才能得到数据?
** 如果先指定的回调, 那当状态发生改变时, 回调函数就会调用, 得到数据
** 如果先改变的状态, 那当指定回调时, 回调函数就会调用, 得到数据

3.5 then的链式调用

Promise实例.then()返回的是一个【新的Promise实例】,它的值和状态由什么决定?
** 1.简单表达: 由then()所指定的回调函数执行的结果决定
** 2.详细表达:
** (1)如果then所指定的回调返回的是非Promise值a:
** 那么【新Promise实例】状态为:成功(fulfilled), 成功的value为a
** (2)如果then所指定的回调返回的是一个Promise实例p:
** 那么【新Promise实例】的状态、值,都与p一致
** (3)如果then所指定的回调抛出异常:
** 那么【新Promise实例】状态为rejected, reason为抛出的那个异常

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
  <script>
const p = new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve('a')------------------1,结果是成功了 --------------
},1000)
})
//----------------1.成功了调用的是 resolve ---------------------------
p.then(
//------------------返回的结果是promise值---------------------
value => {console.log('成功了1',value); return Promise.reject('a')},
reason => {console.log('失败了1',reason);}
//----因为返回的是promise值所以调用的是 reject--然后返回的是100是非promise的值 --------
).then(
value => {console.log('成功了2',value);return true},
reason => {console.log('失败了2',reason); return 100}
//----非promise的值 走的是resolve 然后抛出异常 抛出异常算失败
).then(
value => {console.log('成功了3',value);throw 900},
reason => {console.log('失败了3',reason); return false}

//上面的返回的结果是抛出异常 算失败 所以调用的是reject
).then(
value => {console.log('成功了4',value);return -100},
reason => {console.log('失败了4',reason);}
)

</script>

3.6中断promise链条

如果我们调用了很多then方法 ,但是我们想在第一次失败后就终止后面的调用,但是无论成功还是失败都会返回一个promise 实例,这个实例要么是成功的要么是失败的都会调用下一个then

解决方法:在失败的方法里面返回一个新的promise对象 这样它就是初始的值

value => {console.log(‘成功了1’,value); return Promise.reject(‘a’)},
** **reason => {console.log(‘失败了1’,reason),retunr new promise(()=>{})}

每一个是失败的方法里面都返回一个 new的promise实例

1
2
value => {console.log('成功了1',value); return Promise.reject('a')},
reason => {console.log('失败了1',reason),retunr new promise(()=>{})}

3.7错误的穿透

**就是在链式调用的时候,如果某一个阶段出现了错误,我们就一直往后推,一直推到最后那个兜底的那个那里,这样就避免了没一个then后面的 **

4.async

  1. async修饰的函数
    ** 函数的返回值为promise对象
    ** Promise实例的结果由async函数执行的返回值决定
  2. await表达式
    ** await右侧的表达式一般为Promise实例对象, 但也可以是其它的值
    ** (1).如果表达式是Promise实例对象, await后的返回值是promise成功的值
    ** **(2).如果表达式是其它值, 直接将此值作为await的返回值
  3. 注意:
    ** await必须写在async函数中, 但async函数中可以没有await
    ** 如果await的Promise实例对象失败了, 就会抛出异常, 需要通过try…catch来捕获处理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
<script type="text/javascript">
const p1=new Promise((resolve,reject)=>{
//里面放的是异步的函数
setTimeout(()=>{
resolve(100);
},1001)
})
const p2=new Promise((resolve,reject)=>{
//里面放的是异步的函数
setTimeout(()=>{
reject('第二步出现错误');
},1001)
})

//await 是等待的意思 ,等待后面的执行,但是它只接受成功的数据
//并且它只在 async 修饰的函数里面运行



async function demo(){
try{
//可能出现错误的代码

//等待接收从上面返回的成功的数据
const result1=await p1;
//输出从上面代码返回的值
console.log(result1);

const result2=await p2;
console.log(result2);

//如果上面返回的是失败的值 就会被catch捕捉 然后 统一在catch里面处理
}catch(error){
//输出从try中捕捉的错误
console.log(error);
}
}
demo()


//es6简写
// (async()=>{
// try{
// const result1=await p;
// //输出从上面代码返回的值
// console.log(result1);

// const result2=await p2;
// console.log(result2);

// }catch(error){
// console.log(error);
// }
// })()
</script>

小练习

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
function sendAjax(url,data){
return new Promise((resolve,reject)=>{
//实例xhr
const xhr = new XMLHttpRequest()
//绑定监听
xhr.onreadystatechange = ()=>{
if(xhr.readyState === 4){
if(xhr.status >= 200 && xhr.status < 300) resolve(xhr.response);
else reject(`请求出了点问题`);
}
}
//整理参数
let str = ''
for (let key in data){
str += `${key}=${data[key]}&`
}
str = str.slice(0,-1)
xhr.open('GET',url+'?'+str)
xhr.responseType = 'json'
xhr.send()
})
}

(async()=>{
try {
const result1 = await sendAjax('https://api.apiopen.top/getJoke',{page:1})
console.log('第1次请求成功了',result1);
const result2 = await sendAjax('https://api.apiopen.top/getJoke',{page:1})
console.log('第2次请求成功了',result2);
const result3 = await sendAjax('https://api.apiopen.top/getJoke',{page:1})
console.log('第3次请求成功了',result3);
} catch (error) {
console.log(error);
}
})()

4.2 async 与啊await 使用

  1. async修饰的函数
    ** 函数的返回值为promise对象
    ** Promise实例的结果由async函数执行的返回值决定
  2. await表达式
    ** await右侧的表达式一般为Promise实例对象, 但也可以是其它的值
    ** (1).如果表达式是Promise实例对象, await后的返回值是promise成功的值
    ** **(2).如果表达式是其它值, 直接将此值作为await的返回值
  3. 注意:
    ** await必须写在async函数中, 但async函数中可以没有await
    ** 如果await的Promise实例对象失败了, 就会抛出异常, 需要通过try…catch来捕获处理

总结 被async修饰的函数 返回值值promise实例

await 要结合async使用 不然脑子就抽抽了 注意try catch的使用

4.3await 的原理

若我们使用async配合await这种写法:
** 1.表面上不出现任何的回调函数**
** 2.但实际上底层把我们写的代码进行了加工,把回调函数“还原”回来了。**
** 3.最终运行的代码是依然有回调的,只是程序员没有看见。**

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
const p = new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve('a')
},4000)
})

async function demo(){
//程序员“轻松”的写法
const result = await p
console.log(result);
console.log(100);
console.log(200);

//浏览器翻译后的代码
/* p.then(
result => {
console.log(result);
console.log(100);
console.log(200);
},
) */

}
demo()
console.log(1);

4.4 宏队列与微队列

程序的执行都是有顺序的,在主线程上的是主线程是优先执行的,除此之外还有很多回回调,这些回到会被加入到队列中,但是队列有两种,一种是宏队列,一种是微队列,但是微队列的执行优先级要高于宏队列,当微队列里面的回调执行完了才会开始执行宏队列

宏队列:[宏任务1,宏任务2…..]
微队列:[微任务1,微任务2…..]
规则:每次要执行宏队列里的一个任务之前,先看微队列里是否有待执行的微任务
** 1.如果有,先执行微任务
** ** 2.如果没有,按照宏队列里任务的顺序,依次执行**


//当程序开始的时候。settimeout ,promise 的两个和console.log(主线程)都会瞬间执行完成 然后才会执行各自的回调,promise的回调是属于是微队列,settimeout的回调会加载到宏队列,promise的回调回加载到微队列

//所以程序的执行的结果是主线程 然后两个加载到微队列的promise回调 在然后是settimeout的回调

setTimeout(()=>{
console.log('timeout')
},0)
Promise.resolve(1).then(
value => console.log('成功1',value)
)
Promise.resolve(2).then(
value => console.log('成功2',value)
)
console.log('主线程')