lzpDailyNotes

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

1.同步和异步是什么:

​ ①同步:同步是指如果一个进程在执行某个请求的时候,如果该请求需要等待一段时间,那么该进程会一直等待下去,直到收到返回信息才继续执行下去

​ ②异步: 指一个请求在执行某个请求的时候,即使该请求需要等待一段时间,该请求也不会阻塞下面的请求,而是一直执行下去

2.异步函数发展史

​ callback --> generator --> promise和then -->async和await

3.常见的异步方式:

​ ①定时器 // setTimeout(fn,time) 、setInterval(fn,time)

​ ②接口调用 //ajax

​ ③事件函数 //事件监听事件

4.多次异步调用的结果:

​ ①多次异步调用的结果会导致顺序可能不同步

​ ②异步调用的结果如果存在依赖,则需要嵌套。当进行多次回调函数时,会出现回调地狱

5.Promise的概述:

​ ①背景:JavaScript是单线程的,当执行一个请求,如果该请求的请求时间过长,该应用就会变得卡顿,那么让代码异步执行就变得很有必要了。

​ ②介绍:

​ 1. Promise是一个构造函数,接受一个参数作为对象,该函数的两个参数分别是 resolve和reject 。

​ 2.Promise身上有all、reject、resolve这几个眼熟的方法,原型上有then、catch等同样很眼熟的方法。

​ 3.promise有三种状态,pending、fulfilled、rejected,状态改变只能是pending->fulfilled或者 pending->rejected , 状态一旦改变则不能再变。(要想改变得用return返回一个异步函数)

​ ③作用:
​ 1.解决回调地狱的问题

​ 2.链式调用

6.Promise的注意事项:

   var p = new Promise(function(resolve, reject){
        //做一些异步操作
        setTimeout(function(){
            console.log('异步任务执行完成');
            resolve('随便什么数据2');
        }, 2000);
    });
    

​ 运行代码,会在2秒后输出“异步任务执行完成”。注意!我只是new了一个对象,并没有调用它,我们传进去的函数就已经执行了,这是需要注意的一个细节。所以我们用Promise的时候一般是包在一个函数中,在需要的时候去运行这个函数,如:

function runAsync(){
    var p = new Promise(function(resolve, reject){
        //做一些异步操作
        setTimeout(function(){
            console.log('执行完成');
            resolve('随便什么数据');
        }, 2000);
    });
    return p;            
}
runAsync()

7.Promise的基本用法:

​ (1)使用new实例化一个Promise对象,Promise的构造函数中传递一个参数。这个参数是一个函数,该函数用于处理异步任务。

​ (2)并且传入两个参数:resolve和reject,分别表示异步执行成功后的回调函数和异步执行失败后的回调函数;

​ (3)通过 promise.then() 处理返回结果。这里的 p 指的是 Promise实例。

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>Document</title>
    </head>
    <body>
        <script>
          function getAsync(){
            const promise = new Promise((resolve, reject) => {
                // 这里做异步任务(比如ajax 请求接口。这里暂时用定时器代替)
                setTimeout(function() {
                   // 接口返回的数据
                    var data = { retCode: 0, msg: 'qianguyihao' };
                    if (data.retCode == 0) {
                        // 接口请求成功时调用
                        resolve(data);
                    } else {
                        // 接口请求失败时调用
                        reject({ retCode: -1, msg: 'network error' });
                    }
                }, 100);
            });
            return promise
          }
            // 第二步:业务层的接口调用。这里的 data 就是 从 resolve 和 reject 传过来的,也就是从接口拿到的数据
            getAsync().then(data => {
                // 从 resolve 获取正常结果
                console.log(data);
            }).catch(data => {
                // 从 reject 获取异常结果
                console.log(data);
            });
        </script>
    </body>
</html>

8.Promise的链式用法

​ 解释:链式用法就是当一个异步执行完毕再执行另外一个异步

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>Document</title>
    </head>
    <body>
        <script>
          function getAsync(param){
            const promise = new Promise((resolve, reject) => {
                // 这里做异步任务(比如ajax 请求接口。这里暂时用定时器代替)
                setTimeout(function() {
                   // 接口返回的数据
                    var data = { retCode: 0, msg: 'qianguyihao' };
                    if (data.retCode == 0) {
                        // 接口请求成功时调用
                        resolve(data);
                        console.log(param);
                    } else {
                        // 接口请求失败时调用
                        reject({ retCode: -1, msg: 'network error' });
                    }
                }, 1000);
            });
            return promise
          }

            

            // 第二步:业务层的接口调用。这里的 data 就是 从 resolve 和 reject 传过来的,也就是从接口拿到的数据
            getAsync("1").then(data => {
                // 从 resolve 获取正常结果
                return getAsync("2")
            }).then(data => {
                // 从 resolve 获取正常结果
                return getAsync("3")
            })
        </script>
    </body>
</html>

9.promise链式用法的缺点:

​ 对于不熟悉promise的人来说,简直就是噩梦,因此才出了这一篇博文,有不足之处望提出,会加以改进

10.Promise的常用api

​ 1.Promise.all():并发处理多个异步任务,所有任务都执行成功,才能得到结果。

​ 2.Promise.race(): 并发处理多个异步任务,只要有一个任务执行成功,就能得到结果。

###Promise.all()
<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>Document</title>
    </head>
    <body>
        <script type="text/javascript">
            /*
              封装 Promise 接口调用
            */
            function queryData(url) {
                return new Promise((resolve, reject) => {
                    var xhr = new XMLHttpRequest();
                    xhr.onreadystatechange = function() {
                        if (xhr.readyState != 4) return;
                        if (xhr.readyState == 4 && xhr.status == 200) {
                            // 处理正常结果
                            resolve(xhr.responseText);
                        } else {
                            // 处理异常结果
                            reject('服务器错误');
                        }
                    };
                    xhr.open('get', url);
                    xhr.send(null);
                });
            }

            var promise1 = queryData('http://localhost:3000/a1');
            var promise2 = queryData('http://localhost:3000/a2');
            var promise3 = queryData('http://localhost:3000/a3');

            Promise.all([promise1, promise2, promise3]).then(result => {
                console.log(result);
            });
        </script>
    </body>
</html>
###Promise.race
<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>Document</title>
    </head>
    <body>
        <script type="text/javascript">
            /*
              封装 Promise 接口调用
            */
            function queryData(url) {
                return new Promise((resolve, reject) => {
                    var xhr = new XMLHttpRequest();
                    xhr.onreadystatechange = function() {
                        if (xhr.readyState != 4) return;
                        if (xhr.readyState == 4 && xhr.status == 200) {
                            // 处理正常结果
                            resolve(xhr.responseText);
                        } else {
                            // 处理异常结果
                            reject('服务器错误');
                        }
                    };
                    xhr.open('get', url);
                    xhr.send(null);
                });
            }

            var promise1 = queryData('http://localhost:3000/a1');
            var promise2 = queryData('http://localhost:3000/a2');
            var promise3 = queryData('http://localhost:3000/a3');

            Promise.race([promise1, promise2, promise3]).then(result => {
                console.log(result);
            });
        </script>
    </body>
</html>

11.async和await:

​ ①背景:上文说到如果不熟悉promise,那么它的链式调用无异于看天书,这时候async和await就应运而生,很好的解决了这个问题。

​ ②.什么是async和await:

​ async顾名思义是“异步”的意思,async用于声明一个函数是异步的。而await从字面意思上是“等待”的意思,就是用于等待异步完成。并且await只能在async函数中使用。

​ ③async和await的作用:
​ 比promise更直观,更具有可读性

12.async / await的基本用法:

​ 通常async、await都是跟随Promise一起使用的。为什么这么说呢?因为async返回的都是一个Promise对象同时async适用于任何类型的函数上。这样await得到的就是一个Promise对象(如果不是Promise对象的话那async返回的是什么 就是什么,await得到Promise对象之后就等待Promise接下来的resolve或者reject。

 async function testSync() {
2      const response = await new Promise(resolve => {
3          setTimeout(() => {
4              resolve("async await test...");
5           }, 1000);
6      });
7      console.log(response);
8 }
9 testSync();//async await test...

13.async / await的深入用法

1.async函数返回一个promise对象,如果在async函数中返回一个直接量,async会通过Promise.resolve封装成Promise对象。
我们可以通过调用promise对象的then方法,获取这个直接量。

async function test(){
    return "Hello World";
}

var result=test();
console.log(result);
//打印Promise { 'Hello World' }

2.如果async函数不返回值:

async function test(){
   
}
var result=test();
console.log(result);
//打印Promise { undefined }

3.await的用法:

①说明:await会暂停当前async的执行,await会阻塞代码的执行,直到await后的表达式处理完成,代码才能继续往下执行。
await后的表达式既可以是一个Promise对象,也可以是任何要等待的值。
如果await等到的是一个 Promise 对象,await 就忙起来了,它会阻塞后面的代码,等着 Promise 对象 resolve,然后得到 resolve 的值,作为 await 表达式的运算结果。
②注意事项:await堵塞非promise的异步函数是无效的
1.基本用法:

function A() {
    return "Hello ";
}

async function B(){
    return "World";
}

async function C(){
    //等待一个字符串
    var s1=await A();
    //等待一个promise对象,await的返回值是promise对象resolve的值,也就是"World"
    var s2=await B();
    console.log(s1+s2);
}

C();
//打印"Hello World"

2.进阶用法:

1.async实现链式回调且没有定义then/catch实现的函数,默认走resolve(),而调用resolve默认会打印传进去的参数的值

​ 结果: 输出11 一秒后再打印22

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
<script>
  async function testSync() {
    console.log(await getAsync("11"));
    console.log(await getAsync("22"));
  }
  function getAsync(param){
      return new Promise((resolve,reject)=>{
        setTimeout(function(){
          resolve(param)
        },1000)
      })
  }
 testSync();
</script>

</body>
</html>

2.async实现链式回调但是定义了then/catch实现的函数,resolve默认输出的是undefined

结果: 输出 "11" undefined

一秒后输出: "22" undefined

<script>
    //async的返回值是一个promise
  async function testSync() {
    console.log(await getAsync("11"));
    console.log(await getAsync("22"));
  }
  function getAsync(param){
      return new Promise((resolve,reject)=>{
        setTimeout(function(){
          resolve(param)
        //   reject(param)
        },1000)
      }).then(function(){
        console.log(param);
      }).catch(function(){
        console.log("err");
      })
  }

 testSync()
</script>

​ 3.处理机制:

​ 区别:函数里面多了个await

​ ①串行处理

 1 async function asyncAwaitFn(str) {
 2     return await new Promise((resolve, reject) => {
 3         setTimeout(() => {
 4             resolve(str)
 5         }, 1000);
 6     })
 7 }
 8 
 9 const serialFn = async () => { //串行执行
10 
11     console.time('serialFn')
12     console.log(await asyncAwaitFn('string 1'));
13     console.log(await asyncAwaitFn('string 2'));
14     console.timeEnd('serialFn')
15 }
16 
17 serialFn();

###string 1
###string 2
###serialFn: 2007.7892ms

​ ②并行处理:

 1 async function asyncAwaitFn(str) {
 2     return await new Promise((resolve, reject) => {
 3         setTimeout(() => {
 4             resolve(str)
 5         }, 1000);
 6     })
 7 }
 8 const parallel = async () => { //并行执行
 9     console.time('parallel')
10     const parallelOne = asyncAwaitFn('string 1');
11     const parallelTwo = asyncAwaitFn('string 2')
12 
13     //直接打印
14     console.log(await parallelOne)
15     console.log(await parallelTwo)
16 
17     console.timeEnd('parallel')
18 
19 
20 }
21 parallel()

### string1
### string2
### parallel: 1009.3232  
###比串行快了一倍

​ 4.错误处理:

​ JavaScript异步请求肯定会有请求失败的情况,上面也说到了async返回的是一个Promise对象。既然是返回一个Promise对象的话那处理当异步请求发生错误的时候我们就要处理reject的状态了。在Promise中当请求reject的时候我们可以使用catch。为了保持代码的健壮性使用async、await的时候我们使用try catch来处理错误。

<script>
  async function catchErr() {
    try {
        const errRes = await new Promise((resolve, reject) => {
            setTimeout(() => {
                reject("http error...");
               }, 1000)}
            )

        }
    catch(err) {
        console.log(err);
    }
 }

 catchErr(); //http error...

</script>

14.宏任务和微任务

​ 1.概念: 宏任务和微任务表示异步任务的两种分类。常见的宏任务: setTimeout、setInterval, 常见的微任务: Promise、then、catch、finally

​ 2.基本执行顺序 : 主线程(外层宏) --> 微 --> 宏

​ ①基本示例1:

//  1  1.1  -  2  -  3     
setTimeout(() => {
    console.log('3')
}, 0)
console.log('1');

new Promise((resolve) => {
    console.log('1.1');
    resolve()
}).then(() => {
    console.log('2');
}).then(()=>{
    console.log('2.1')
})

​ ②基本示例2:

 console.log('1');
    setTimeout(function () {
      console.log('3');
      new Promise(function (resolve) {
        console.log('3.1');
        resolve();
        new Promise(function (resolve) {
            console.log('9.1');
            resolve();
          }).then(function () {
            console.log('9.2')
         })
      }).then(function () {
        console.log('4')
      })
    })

    new Promise(function (resolve) {
      console.log('1.1');
      resolve();
    }).then(function () {
      console.log('2')
    })

    setTimeout(function () {
      console.log('5');
      new Promise(function (resolve) {
        console.log('5.1');
        resolve();
      }).then(function () {
        console.log('6')
      })
    })

​ ③基本示例3:

 console.log('1');
    setTimeout(function () {
      console.log('3');
      new Promise(function (resolve) {
        console.log('3.1');
        resolve();
        new Promise(function (resolve) {
          console.log('9.1');
          resolve();
        }).then(function () {
          console.log('9.2')
        })
      }).then(function () {
        console.log('4')
      })
    })

    new Promise(function (resolve) {
      console.log('1.1');
      setTimeout(function(){
        console.log("8");
      })
      resolve();
    }).then(function () {
      console.log('2')
    })

    setTimeout(function () {
      console.log('5');
      new Promise(function (resolve) {
        console.log('5.1');
        resolve();
      }).then(function () {
        console.log('6')
      })
    })
输出结果是: 1、1.1、2、3、3.1、9.1、9.2、4、8、5、5.1

3.宏任务和微任务的总结:

​ 先执行主线程的东西,宏任务和微任务加入队列,主线程执行完之后,执行微任务队列(先进先出的原则),微任务如果包含宏任务,也先丢到宏任务后面,微任务执行完,最后执行宏任务

参考博客:

https://www.cnblogs.com/lemonib/p/10087356.html

posted on 2022-07-26 23:33  码hey  阅读(1272)  评论(0)    收藏  举报