js中的异步编程

异步编程

预计阅读时间: 5 分钟
"异步编程

异步编程是处理耗时操作(如网络请求、文件读写、数据库操作等)而不阻塞主线程的一种方式。JavaScript是单线程的,这意味着它一次只能执行一个任务。如果所有操作都是同步的,那么在执行耗时操作时,页面会变得无响应,用户体验会非常差。

为什么需要异步编程?

  • 避免阻塞主线程:JavaScript运行在浏览器的主线程上,如果执行耗时操作(如网络请求),主线程会被阻塞,导致页面无法响应用户操作,直到操作完成。
  • 提升性能:通过异步编程,可以在等待耗时操作完成的同时,继续执行其他任务,从而提高程序的整体性能。
  • 改善用户体验:异步操作可以让页面保持响应,用户不会感觉到卡顿或延迟。

Promise

"Promise

Promise 是 JavaScript 中用于处理异步操作的一种机制。它解决了传统回调函数(Callback)模式中的一些问题,使得异步代码更易于编写、阅读和维护

1 Promise的使用

1.1 构造函数的参数

  • excutor本身也是一个函数,会自动传入2个参数,函数体会在new promise时被立即执行
excutor
1const excutor = (resolve, reject) => {} 
2 const promise = new Promise(excutor)

1.2 resolve的值的类型

  • 普通值,直接返回
  • 新的Promise,以新的promise的值为值
  • thenable的对象,即实现了then方法的对象,then方法会被自动调用,且传入resolve,reject参数,resolve的值就是then的结果,reject就是catch结果
resolve的值的类型
1const value1 = 'aaa'
2const valueObj = {name:'aaa'}
3const promiseRes = (resolve,reject)=>{ 
4  setTimeout(() => {
5    resolve('promiseRes')
6  }, 2);
7}
8const promiseErr = (resolve,reject)=>{
9  setTimeout(() => {
10    reject('promiseErr')
11  }, 4);
12}
13
14const thenable = {
15  name: 'thenable',
16  then: (resolve,reject) =>{
17    resolve('thenable')
18  }
19}
20
21function foo(){
22  return new Promise((resolve, reject) => {
23    setTimeout(() => {
24      resolve(value1)  // res :>>  aaa
25      // resolve(valueObj) // res :>>  { name: 'aaa' }
26      // resolve(new Promise(promiseRes)) // res :>>  promiseRes
27      // resolve(new Promise(promiseErr)) // err :>>  promiseErr
28      // resolve(thenable) //res :>>  thenable
29    }, 4);
30  })
31}
32
33foo().then(res=>{
34  console.log('res :>> ', res);
35}).catch(err=>{
36  console.log('err :>> ', err);
37})

1.3 then方法的细节

  • 参数,resolve,reject,分别是成果和失败的回调
  • 一个promise可以使用多个then,每个then都会被回调
  • then后面可以链式操作
  • then/catch里面:
    • 返回一个值,会产生一个新的promise,并把返回值作为resolve的值,链式操作时会作为下一个then的结果
    • throw new Error() 会被下一个最近的catch捕获
then方法的细节
1function foo(m){
2  return new Promise((resolve, reject) => {
3    setTimeout(() => {
4      if(m>3){
5        resolve('succ')
6      }else {
7        reject('err')
8      }
9    }, 4);
10  })
11}
12
13const promise = foo(4)
14// 1. then 可以传入2个参数
15promise.then(res=>{
16  console.log('res1 :>> ', res);
17},err=>{
18  console.log('err :>> ', err);
19})
20//2. 同一个promise,then可以监听多次,都会被回调执行
21promise.then(res=>{
22  console.log('res1 :>> ', res);
23})
24promise.then(res=>{
25  console.log('res2 :>> ', res);
26})
27promise.then(res=>{
28  console.log('res3 :>> ', res);
29})
30// 3. then后面可以链式操作
31promise.then(res=>{
32  console.log('res1 :>> ', res);
33}).catch(err=>{
34  console.log('err :>> ', err);
35})
36
37// 4. then/catch里面再返回一个值,会产生一个新的promise,并把返回值作为resolve的值,
38// 链式操作时会作为下一个then的结果,
39// throw new Error() 会被下一个最近的catch捕获
40promise.then(res=>{
41  console.log('res1 :>> ', res);
42  throw new Error('bad')
43}).catch(err=>{
44  console.log('error1 :>> ', String(err));
45  throw new Error('bad2')
46}).catch(err=>{
47  console.log('error2 :>> ', String(err));
48  return 'from error'
49}).then(res=>{
50  console.log('res2 :>> ', res);
51})

1.4 catch方法的细节

  • 一个promise可以单独多次使用catch,都会被独立回调
  • then后面没有捕获错误,而是如果出现错误则会报错,即使已经单独catch了
catch方法的细节
1function foo(m){
2  return new Promise((resolve, reject) => {
3    setTimeout(() => {
4      if(m>3){
5        resolve('succ')
6      }else {
7        reject('err')
8      }
9    }, 4);
10  })
11}
12
13const promise = foo(3)
14//2. 同一个promise,catch可以监听多次,都会被回调执行
15promise.catch(err=>{
16  console.log('err1 :>> ', err);
17})
18promise.catch(err=>{
19  console.log('err2 :>> ', err);
20})
21promise.catch(err=>{
22  console.log('err3 :>> ', err);
23})
24// 2. then后面如果没有catch,即使promise已经在上面直接catch了,这里的then仍然会报错
25promise.then(res=>{
26  console.log('res1 :>> ', res);
27})

1.5 finally回调

ES9(ES2018)新增的特性 无用论fullfilled还是rejected,都会回调这个函数

1promise.then(res=>{}).catch(()=>{}).finally(()=>{
2//最终一定会执行的方法
3})

2 Promise执行时机

1function foo (m){
2  return new Promise((resolve, reject) => {
3    console.log('immedi',1)
4    setTimeout(() => {
5      console.log('setTimeout0',2)
6    }, 0);
7  
8    setTimeout(() => {
9      if(m>1){
10        console.log('resolve', 3)
11        resolve(1)
12        console.log('after resolve', 4)
13      }else {
14        console.log('reject', 5)
15        reject('错误')
16        console.log('after reject', 6)
17      }
18      console.log('setTimeout3',7)
19    }, 3);
20  
21  })
22
23}
24foo(4).then(res=>{
25  console.log('then', 8)
26}).catch(err=>{
27  console.log('catch', 9)
28})
29console.log('outside',10)
30
31// 执行结果
32// immedi 1
33// outside 10
34// setTimeout0 2
35// resolve 3
36// after resolve 4
37// setTimeout3 7
38// then 8

3 Promise类方法

3.1 Promise.resolve /reject

  • 允许直接使用,不需要new一个实例,返回一个promise

3.2 Promise.all

  • 全部的promise执行完成
  • 返回一个新的promise,状态由所有的promise共同决定,
    • 当所有promise状态是fullfilled时,最终状态为fullfilled,在then中回调所有的执行结果的数组
    • 有一个promisereject,则最终状态为reject,并返回第一个reject结果,可以catch到第一个error

3.3 Promise.allSettled

  • ES11 (ES2012) 新增
  • 全部promise都执行,这个promise的状态一定是fullfilled,结果在then中返回一个数组,包含了所有的state
Promise.allSettled
1const p1 = new Promise((resolve,reject)=>{
2  reject('p1')
3})
4const p2 = new Promise((resolve,reject)=>{
5  reject('p2')
6})
7const p3 = new Promise((resolve,reject)=>{
8  resolve('p3')
9})
10
11Promise.allSettled([p1,p2,p3]).then(res=>{
12  console.log('res', res)
13  // res [
14  //{ status: 'rejected', reason: 'p1' },
15  //{ status: 'rejected', reason: 'p2' },
16  //{ status: 'fulfilled', value: 'p3' }
17  //]
18})

3.4 Promise.race

  • 返回的promise状态由最快的一个promise决定
Promise.race
1const p1 = new Promise((resolve,reject)=>{
2  setTimeout(() => {
3    reject('p1')
4  }, 6);
5})
6const p2 = new Promise((resolve,reject)=>{
7  setTimeout(() => {
8    
9    reject('p2')
10  }, 1);
11})
12const p3 = new Promise((resolve,reject)=>{
13  setTimeout(() => {
14    resolve('p3')
15  }, 2);
16})
17
18Promise.race([p1,p2,p3]).then(res=>{
19  console.log('res', res)
20}).catch(err=>{ 
21  console.log('err', err) // err p2
22})
23
24#### 1.7.5 Promise.any
25返回最快的一个fullfilled,如果没有fullfilled,则最终会是reject状态
26const p1 = new Promise((resolve,reject)=>{
27  setTimeout(() => {
28    reject('p1')
29  }, 6);
30})
31const p2 = new Promise((resolve,reject)=>{
32  setTimeout(() => {
33    
34    reject('p2')
35  }, 1);
36})
37const p3 = new Promise((resolve,reject)=>{
38  setTimeout(() => {
39    resolve('p3')
40  }, 2);
41})
42
43Promise.any([p1,p2,p3]).then(res=>{
44  console.log('res', res) // res p3
45}).catch(err=>{
46  console.log('err', err)
47})

async/await

"async/await原理

实际上是生成器函数的语法糖,并且做了一些优化

生成器函数返回一个迭代器,调用next方法返回每个yield后面的promise,再在promise的then中再次调用next并把结果当成参数传递进去

生成器函数与promise的结合

async用于定义一个异步函数 await只能在异步函数中使用 (ES8),以及在模块顶层使用(ES13)

1async function foo(){
2   await promise
3 }
  • 会等await后面的promise有fullfilled状态,才会继续往下执行