当前位置:   article > 正文

js:Promise理解、async和await的理解、宏任务/微任务、异常处理、防抖和节流_js await

js await

一、Promise

1、Promise的基本使用

Promise对象代表一个异步操作,有三种状态,只有异步操作的结果,可以决定当前是哪一种状态。
这里只是new了一个对象,并没有调用它,我们传进去的函数就已经执行了

new Promise((resolve, reject) => {
  // 异步工作处理
  ...
  // 异步工作执行完之后
  resolve(result) // ——如果成功完成,并返回结果result;
  reject(error) // ——如果执行是失败并产生error;
})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

2、一般都是结合函数使用

我们用Promise的时候一般是包在一个函数中,在需要的时候去运行这个函数

<div onClick={promiseClick}>开始异步请求</div>
 
const promiseClick =()=>{
  console.log('点击方法被调用')
  let p = new Promise(function(resolve, reject){
	//做一些异步操作
	setTimeout(function(){
	  console.log('执行完成Promise');
	  resolve('要返回的数据可以任何数据例如接口返回数据');
	}, 2000);
	});
   return p
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

3、Promise的执行结果获取

语法:promise.then(function(result),function(error))

案例

let promise = new Promise((resolve, reject) => {
  // 异步工作处理
  ...
  // 异步工作执行完之后
  resolve(result) // ——如果成功完成,并返回结果result;
  reject(error) // ——如果执行是失败并产生error;
})
promise
.then(
  (result) => {
    console.log('成功执行,resolve(result)')
  }, 
  (error) => {
    console.log('失败执行,reject(error)')
  }
)
.catch(
  // 区别:还可以获取到then的第一个函数的报错
  (err)=>{console.log('等同于then的第二个error函数')}
)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

4、Promise的链式编程

function myReadFile(path) {
    return new Promise((resolve, reject) => {
        fs.readFile(path, (err, data) => {
            if (err) reject(err)
            console.log(data.toString())
            resolve(data)
        })
    })
}

myReadFile('1.txt')  // 返回Promise
    .then(data => { return myReadFile('2.txt') })  // 再次调用myReadFile,返回Promise
    .then(data => { return myReadFile('3.txt') })  // 依次类推
    .then(data => { return myReadFile('4.txt') })
    .then(data => { return myReadFile('5.txt') })
    .then(data => { return myReadFile('6.txt') })
    .then(data => { return myReadFile('7.txt') })
    .then(data => { return myReadFile('8.txt') })
    .then(data => { return myReadFile('9.txt') })
    .then(data => { return myReadFile('10.txt') })
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

注意:

  1. promise.then()的返回值其实是一个Promise对象,这个新Promise的决议是等到then方法传入的回调函数有返回值时, 进行决议(要有return 数据)。
  2. 当promise进行链式调用时promise.then().then(),第二个then的res其实是第一个then方法里的return的数据,包装成Promise。参考下面demo,理解链式调用
  3. catch()只需要一个就可以了,抛出异常可以用throw new Error("第三个Promise的异常error")
    const promise = new Promise((resolve, reject) => {
      resolve("aaaaaaa")
      // reject()
    })

    // 1.then方法是返回一个新的Promise, 这个新Promise的决议是等到then方法传入的回调函数有返回值时, 进行决议
    // Promise本身就是支持链式调用
    // then方法是返回一个新的Promise, 链式中的then是在等待这个新的Promise有决议之后执行的
    promise.then(res => {
      console.log("第一个then方法:", res) // 打印 "aaaaaaa"
      return "bbbbbbbb"
    }).then(res => {
      console.log("第二个then方法:", res) // 打印 "bbbbbbbb"
      return "cccccccc"
    }).then(res => {
      console.log("第三个then方法:", res) // 打印 "cccccccc"
    }).catch(err => {
      console.log("catch回调被执行:", err)
    })
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

5、异步和同步

假设现在有A、B两个任务需要处理,使用并行、同步和异步的处理方式会分别采用如下图所示的执行方式:
在这里插入图片描述

6、Promise.all

Promise.all:多个异步请求,如果有一个错误,会在catch里返回错误结果,但多个请求仍然会发出请求。
应用场景:基本是在多个请求中【获取数据】时才会用到,直接提交的接口不要用

Promise.all的参数的执行顺序结果的返回顺序
执行顺序:谁执行的快谁先打印结果。
返回顺序:按照promise的添加顺序返回

function promise1 () {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve('1');
    }, 10000);
  })
}
function promise2 () {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve('2');
    }, 1000);
  })
}

Promise.all([promise1(), promise2()]).then(function(results){
  console.log(results); // results:['1', '2']
});
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

二、async和await的理解

1、async

async是一个加在函数前的修饰符,被async定义的函数会默认返回一个Promise对象resolve的值

1.1 return:Promise对象

async function fun0(){
  return new Promise(function(resolve,reject){
    resolve('Promise对象')
})
}

console.log(fun0());  // Promise {<fulfilled>: 'Promise对象'}
fun0().then(res=>console.log(res));  // 通过then获取结果:Promise对象
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

1.2 return:常规变量(包装在Promise中)

async function fun0(){
  return '常规变量';
}

console.log(fun0());  // Promise {<fulfilled>: '常规变量'}
fun0().then(res=>console.log(res));  // 通过then获取结果:常规变量
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

1.3 无返回值:返回underfind

async function fun0(){}

console.log(fun0());  // Promise {<fulfilled>: undefined}
fun0().then(res=>console.log(res));  // 通过then获取结果:undefined
  • 1
  • 2
  • 3
  • 4

2、await

await 也是一个修饰符,只能放在async定义的函数内。可以理解为等待。

2.1 await 修饰Promise对象

它会阻塞后面的代码,等Promise中返回的内容(resolve或reject的参数),且取到值后语句才会往下执行

async function fun(){
  let a = await 1;
  let b = await new Promise((resolve,reject)=>{
      setTimeout(function(){
          resolve('setTimeout')
      },3000)
  })
  let c = await function(){
      return 'function'
  }()
  console.log(a,b,c)
}
fun(); // 3秒后输出: 1 "setTimeout" "function"
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

2.2 await 不是Promise对象

把这个非promise的东西当做await表达式的结果。(相当于没有await)

function log(time){      // 普通的异步函数
  setTimeout(function(){
      console.log(time);
  },time)
}
async function fun(){
  let a = await log(1000); // 按照异步函数的处理事件快慢决定打印殊勋
  let b = await log(3000);
  let c = log(2000);
  console.log(a,b,c);  // 先执行它,上面是三个都是异步,所以开始abc都是undefined
}
fun(); 
// 立即输出 undefined undefined undefined
// 1秒后输出 1000
// 2秒后输出 2000
// 3秒后输出 3000
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

3、promise和async/await 的区别

3.1 单一的Promise链,两者区别不大

// Promise 写法
function p(){
  return new Promise((resolve,reject)=>{
    setTimeout(()=>{resolve('Promise写法')},1000)
  })
}
p().then(res=>{console.log(res)})


// async/await 写法
function a(){
  return new Promise((resolve,reject)=>{
    setTimeout(()=>{resolve('async/await写法')},1000)
  })
}
async function fn() {
  const res = await a()
  console.log(res);
}
fn()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

3.2 多个 Promise链,async/await阅读友好

// Promise 写法
function p(time){
  return new Promise((resolve,reject)=>{
    setTimeout(()=>{
      console.log('Promise写法' + time);
      resolve('Promise返回数据')},time)
  })
}

p(1000)
  .then(res=>{return p(2000)})
  .then(res=>{return p(3000)})
  .then(res=>{return p(4000)})
  .then(res=>{return p(5000)})

// async/await 写法
function a(time){
  return new Promise((resolve,reject)=>{
    setTimeout(()=>{
      console.log('async/await写法' + time);
      resolve('async/await返回数据')},time)
  })
}
async function fn() {
  const res1 = await a(1000)
  const res2 = await a(2000)
  const res3 = await a(3000)
  const res4 = await a(4000)
  const res5 = await a(5000)
}
fn()
  • 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

4、promise和async/await 正确用法

前两步骤都是一样的,promise直接执行,async/await 通过第三方函数执行

4.1 promise

  1. fn函数要return一个promise对象:[return new Promise()]
  2. 在promise对象中进行异步处理,通过resolve/reject出去结果/错误
  3. 调用fn函数,通过then方法获取结果/错误

4.2 async/await

  1. fn1函数要return一个promise对象:[return new Promise()]
  2. 在promise对象中进行异步处理,通过resolve/reject出去结果/错误
  3. 定义一个async fn2函数,函数里awaitfn1函数
  4. 通过变量接收fn1函数的结果
  5. 调用(执行)fn2 函数

三、宏任务/微任务

除了主任务栈,事件循环中并非只维护着一个队列,事实上是有两个队列:

  • 宏任务队列(macrotask queue):ajaxsetTimeoutsetIntervalDOM监听UI Rendering
  • 微任务队列(microtask queue):Promise的then回调Mutation Observer APIqueueMicrotask()

注意:

  1. async/await中,视为同步代码(await上面和右边),在主任务栈里执行。但await前后会有区分,看第2条说明
  2. 执行到async函数时,await上面和右边的内容会在主任务栈里执行,await右边和下面的内容,会在Promise有返回结果时,加入到微任务中开始执行
  3. Promise对象里的代码,是同步执行的,只有then回调是微任务。
  4. 调用then之前,会先将Promise内部的代码执行完才会执行到then。或者说必须是执行到了resolve()才可以。
  5. await是会阻塞后面的代码执行,必须等到promise的结果resolve()(类似于第4条then的执行顺序)

主任务栈宏任务队列微任务队列,他们三个的执行顺序:

  1. 先执行主任务栈
  2. 宏任务队列开始前,需要先把微任务队列清空
  3. 宏任务开始后,是加入到主任务栈中执行,视为主任务,所以中间有宏/微任务的话,继续添加到对应的队列中等待即可
  4. 最后执行微任务队列
  5. 宏/微任务队列都是先加入的先执行

四、异常处理

  <script>
    function foo() {
      console.log("foo function1")
      throw new Error("我是错误信息")
      console.log("foo function2")
      console.log("foo function3")
      console.log("foo function4")
    }

    function test() {
      // 自己捕获了异常的话, 那么异常就不会传递给浏览器, 那么后续的代码可以正常执行
      try {
        foo()
        console.log("try后续的代码")
      } catch(error) {
        console.log("catch中的代码")
        // console.log(error)
      } finally {
        console.log("finally代码")
      }
    }

    test()

    console.log("--------")

  </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

注意:

  1. throw抛出异常信息
  2. new Error("自定义错误信息")生成异常信息
  3. try/catch/finally接收异常

五、防抖和节流

防抖:只有在某个时间内,没有再次触发某个函数时,才真正的调用这个函数;
(等待最后一次才进行函数调用)

function debounce(fn, delay){
     let timerId = null
     return function(){
         const context = this
         //如果接到订单就再等3分钟
         if(timerId){window.clearTimeout(timerId)}
         //3分钟没有接到订单就直接配送
         timerId = setTimeout(()=>{
             fn.apply(context, arguments)
             timerId = null
         },delay)
     }
 }

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

节流:在某个时间内(比如500ms),某个函数只能被触发一次;
(按照一定的频率进行调用)

function throttle(fn, delay){
     // 设置一个触发开关
     let canUse = true
     return function(){
     //如果为true,就触发技能,否则就不能触发
         if(canUse){
             fn.apply(this, arguments)
             //触发技能后,关闭开关
             canUse = false
             //在3秒后打开开关
             setTimeout(()=>{canUse = true}, delay)
         }
     }
 }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/菜鸟追梦旅行/article/detail/565754
推荐阅读