• 整理


    Promise 和 Async/Await用法整理

    1. Promise

    1.1 简介

    Promise,简单来说就是一个容器,里面保存着某个未来才会结束得事件(通常是一个异步操作得结果)

    Promise 对象得基本语法:

    new Promise( (resolve, reject) => {
        // ...
    });
    

    从语法上来说,Promise 是一个对象,从它可以获取异步操作得信息。

    基本语法:

    let p = new Promise( (resolve, reject) => {
        // ...
        resolve('success');
    });
    
    p.then( result => {
        console.log(result); // success
    })
    

    Promise对象特点和三个状态:

    • Pending
    • value -> Fulfilled
    • error -> Rejected

    例如:

    let p = new Promise( (resolve, reject) => {
        // ...
        resolve('success');
        console.log('after resolve');
        reject('error');
    });
    
    p.then( result => {
        console.log(result);
    });
    
    p.catch( result => {
        console.log(result);
    })
    

    结果:
    after resolve;
    success;

    resolve 下面得语句其实是可以执行的,那么为什么reject的状态信息在下面没有接收到呢?这就是因为 Promise 对象的特点:状态的凝固。 new 出一个 Promise 对象时,这个对象的起始状态就是 Pending 状态,在根据 resolve 或 reject 返回 Fulfilled 状态/ Rejected 状态。

    1.2 then

    Promise.prototype.then
    它的作用是为 Promise 实现添加状态改变时的回调函数,参数个数可以有两个。

    • then(func1, func2)
    • then(func1)
    • then(null, func2)

    Then 分别接受 resolve 和 reject 的信息,有三种参数形式,第三种比较“怪异”,只用来接受做 reject 处理。

    eg:

    let p = new Promise( (resolve, reject) => {
        // ...
        let random = Math.random(); // 小于1大于0
        if( random > 0.4 ){
            resolve('random > 0.4')
        } else {
            reject('random <= 0.4')
        }
    });
    
    p.then( result => {
        console.log('resolve', result);
    }, result => {
        console.log('reject', result);
    });
    

    1.3 链式调用

    我们来执行一段代码:

    let p = new Promise( (resolve, reject) => {
        reject('reject');
    });
    
    let resultP = p.then(null, result => {
        console.log(result);
    });
    
    console.log(resultP);
    

    结果:

    Promise {}
    reject

    js的执行顺序就是这样, 同步 -> 异步 -> 回调,在同步执行的时候,Promise 对象还处于 pending 的状态,也说明了这个 then 返回的是一个 Promise 对象。

    而且必须在 then 里面给一个Promise,才能继续调用,否则 undefined。

    eg:

    let p = new Promise((resolve, reject) => {
      reject("error");
    });
    let resultP = p.then(null, (result) => {
      console.log(result);
      return new Promise((resolve, reject) => {
        reject("error");
      });
    });
    
    resultP.then(
      (tmp) => {
        console.log(tmp, 1);
      },
      (tmp) => {
        console.log(tmp, 2);
      }
    );
    

    结果:

    error
    error 2

    1.4 catch

    Promise.prototype.catch
    用于指定Promise 状态变为rejected 时的回调函数,可以被认为是then(null, func2)的简写形式。

    eg:

    
    let p = new Promise((resolve, reject) => {
      reject("error");
    });
    p.catch((result) => {
      console.log(result, 0);
    });
    p.then(null, (tmp) => {
      console.log(tmp, 1);
    });
    p.catch((result) => {
      console.log(result, 2);
    });
    

    0
    1
    2

    1.5 Promise.resolve()

    将现有对象转为一个resolve状态的 Promise 对象的快捷方式。
    传入一个普通的对象:

    let p1 = Promise.resolve({
      name: "lemon",
      age: "xxxx",
    });
    p1.then((result) => {
      console.log(result);
    });
    
    

    {name: 'lemon', age: 'xxxx'}

    如果是Promise对象呢,直接返回

    let p = new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve("success");
      }, 500);
    });
    let pp = Promise.resolve(p);
    pp.then((result) => {
      console.log(result);
    });
    console.log(pp === p);
    

    true
    success

    1.6 Promise.reject()

    
    let p = Promise.reject(123);
    console.log(p);
    p.then((result) => {
      console.log(result);
    }).catch((result) => {
      console.log("catch", result);
    });
    
    
    

    Promise { 123 }
    catch 123

    1.7 Promise.all()

    Promise.all 方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。成功和失败的返回的数据类型是不相同的。

    eg:

    let p1 = Promise.resolve(123);
    let p2 = new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve("hello");
      }, 500);
    });
    let p3 = Promise.resolve("success");
    Promise.all([p1, p2, p3]).then(
      (result) => {
        console.log(result);
      },
      (error) => {
        console.log(error);
      }
    );
    console.log("---");
    
    

    '---'
    [123, 'hello','success']

    成功之后就是数组类型,当所有状态都是成功状态才返回数组,只要其中有一个的对象是 reject 的,就返回 reject 的状态值。

    eg:

    let p1 = Promise.resolve(123);
    let p2 = new Promise((resolve, reject) => {
      setTimeout(() => {
        reject("hello");
      }, 500);
    });
    let p3 = Promise.resolve("success");
    Promise.all([p1, p2, p3]).then(
      (result) => {
        console.log(result);
      },
      (error) => {
        console.log(error);
      }
    );
    

    'hello'

    有一个eg:

    //用sleep来模仿浏览器的AJAX请求
    function sleep(wait) {
      return new Promise((res, rej) => {
        setTimeout(() => {
          res(new Date());
        }, wait);
      });
    }
    let p1 = sleep(5000);
    let p2 = sleep(10000);
    let p3 = sleep(20000);
    Promise.all([p1, p2, p3]).then((result) => {
      console.log(result);
      //.....
      //loading
    });
    

    58:55
    59:01
    59:11

    可以看出 Promise.all 中的 Promise 几乎是同时开始的

    1.8 Promise.race()

    Promise.race 方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例。

    和 all 同样接受多个对象 , 不同的是,race()接受的对象中,那个对象返回的快就返回那个对象,就如race直译的赛跑这样。如果对象其中有 reject 状态的,必须 catch 捕捉到,如果返回的够快,就返回这个状态。race 最终返回的只有一个值。

    eg:

    
    let p1 = sleep(500);
    let p0 = sleep(2000);
    let p2 = new Promise((resolve, reject) => {
      setTimeout(() => {
        reject("error");
      }, 1000);
    });
    Promise.race([p1, p0]).then((result) => {
      console.log(result);
    });
    Promise.race([p2, p0])
      .then((result) => {
        console.log(result);
      })
      .catch((result) => {
        console.log(result);
      });
    Promise.race([p1, p2])
      .then((result) => {
        console.log(result);
      })
      .catch((result) => {
        console.log(result);
      });
    
    

    500
    500
    error

    1.9 异常处理

    错误处理的声音实在安静,安静得听不见 from Nolan Lawson

    当 promise 被明确拒绝时,会发生拒绝;但是如果是在构造函数回调中引发得错误,则会隐式拒绝。

    为什么说安静,一个例子,Promise 内部得错误外界用try-catch捕捉不到。可用用then或catch捕获到。

    eg:

    try {
      let p = new Promise((resolve, reject) => {
        throw new Error(" I'm error");
      });
      p.then(
        (res) => {
          console.log("resolve", res);
        },
        (error) => {
          console.log("reject", error);
          p.catch((reject) => {
            console.log("Promise.catch");
          });
        }
      );
    } catch (error) {
      console.log("catch", error);
    }
    
    

    reject Error: I'm error
    Promise.catch

    所以:
    建议,在 promise的链的尾部必须要有个 catch 接着

    2. Async-Await

    2.1简介

    Async-Await:
    异步编程的最高境界,就是根本不用关心它异步。async 函数就是隧道尽头的亮光,很多人认为它是异步操作的终极解决方案。

    async-await 与 promise 的关系:
    不存在谁替代谁,因为 async-await 是寄生于 Promise。Generator 的语法糖。

    async 和 await 在干什么,async 用于声明一个 function 是异步的,而 await 可以认为是 async wait 的简写, 等待一个异步方法执行完成。

    2.2 基本语法

    async function demo(params) {
    
    }
    
    demo();
    

    async 函数返回的是一个 Promise 对象。
    必须了解的 asyncFunction

    console.log(async function(){}.constructor);
    

    在Chrome里申明这样一个函数,可以在控制台看到返回的其实就是一个Promise对象。
    扩展需要了解的就是Chrome现在也支持asyncFunction,可以在Chrome控制台测试:
    console.log(async function(){}.constructor);
    ƒ AsyncFunction() { [native code] }

    2.3 规则

    1. async 表示这是一个 async 函数, await 只能用在这个函数里面。
    2. await 表示在这里等待 promise 返回结果后,再继续执行。
    3. await 后面跟着的应该是一个 promise 对象(当然,其他返回值也没关系,只是会立即执行,不过那样就没有意义了...)
    await放在普通函数里是会报错的。

    await可以理解为 async wait 的简写。await 必须出现在 async 函数内部,不能单独使用。

    await等待的虽然是 promise 对象,但不必写.then(...),直接可以得到返回值。

    eg:

    async function demo() {
      let result = await Promise.resolve(123);
      console.log(result);
    }
    demo();
    
    

    2.4 应用

    Promise 虽然一方面解决了 callback 的回调地狱,但是相对的把回调“纵向发展”了,形成了一个回调链。

    eg:

    
    function sleep(wait) {
      return new Promise((res, rej) => {
        setTimeout(() => {
          res(wait);
        }, wait);
      });
    }
    sleep(100)
      .then((result) => {
        return sleep(result + 100);
      })
      .then((result02) => {
        return sleep(result02 + 100);
      })
      .then((result03) => {
        console.log(result03);
      });
    
    

    300

    后面的结果都是依赖前面的结果。
    改成 async/await 写法就是:

    
    function sleep(wait) {
      return new Promise((res, rej) => {
        setTimeout(() => {
          res(wait);
        }, wait);
      });
    }
    async function demo() {
      let result01 = await sleep(100);
      // 上一个await执行之后才会执行下一句
      let result02 = await sleep(result01 + 100);
      let result03 = await sleep(result02 + 100);
      return result03;
    }
    demo().then((result) => {
      console.log(result);
    });
    
    

    因为 async 返回的也是 promise 对象,所以用 then 接受就行了。

    300

    需要注意的就是 await 是强制把异步变成了同步,这一句代码执行完,才会执行下一句。

    2.5 错误处理

    既然 .then(...) 不用写了,那么 .catch(...) 也不用写,可以直接用标准的 try catch 语法捕捉错误。

    
    async function demo() {
      let result01 = await sleep(100);
      // 上一个await执行之后才会执行下一句
      let result02 = await sleep(result01 + 100);
      let result03 = await sleep(result02 + 100);
      try {
        let result04 = await new Promise((resolve, reject) => {
          setTimeout(() => {
            reject(100);
          }, 100);
        });
        return result04;
      } catch (e) {
        console.log("error", e);
      }
    }
    let p = demo().then(
      (data) => {
        console.log(data, "1");
      },
      (e) => {
        console.log(e, "2");
      }
    );
    
    

    结果:

    error 100
    undefined "1"

    这是基本的错误处理,但是当内部出现一些错误时,包括有一个Promise reject 了,就会抛出错误且下面的代码不会执行。

    
    function sleep(wait) {
      return new Promise((res, rej) => {
        setTimeout(() => {
          rej(wait);
        }, wait);
      });
    }
    async function demo() {
      let a = await sleep(100);
      let b = await sleep(200);
      console.log(2);
    }
    demo();
    
    

    Uncaught (in promise) 100

    处理异常时,如果 async 中已经处理过了则 .catch()将捕捉不到。如果 async 没有处理,则 .catch()可以捕捉到。

    eg. async 中处理:

    
    function sleep(wait) {
      return new Promise((res, rej) => {
        setTimeout(() => {
          rej(wait);
        }, wait);
      });
    }
    async function demo() {
      try {
        let result04 = await sleep(100);
      } catch (e) {
        console.log("error", e);
      }
    }
    let p = demo().catch((e) => {
      console.log("promise catch", e);
    });
    
    

    'error' 100

    eg. async 外部处理:

    
    function sleep(wait) {
      return new Promise((res, rej) => {
        setTimeout(() => {
          rej(wait);
        }, wait);
      });
    }
    async function demo() {
      let result04 = await sleep(100);
    }
    let p = demo().catch((e) => {
      console.log("promise catch", e);
    });
    
    

    'promise catch' 100

    2.6 注意你的并行执行和循环

    如果这三个是你想异步发出的AJAX请求,在这段代码里其实是同步的,第一个发出去被处理后( resolve/reject )才会处理第二个,所以 async / await 需要谨慎使用。

    eg.1

    
    function sleep(wait) {
      return new Promise((resolve, rej) => {
        console.log(`wait:  ${wait}`);
        setTimeout(() => {
          resolve(wait);
          console.log(`resolve: ${wait}`);
        }, wait);
      });
    }
    async function demo() {
      let result01 = await sleep(10000);
      let result02 = await sleep(20000);
      let result03 = await sleep(30000);
    }
    let p = demo().catch((e) => {
      console.log("promise catch", e);
    });
    
    

    wait: 10000
    一段时间后
    resolve: 10000
    wait: 20000
    一段时间后
    resolve: 20000
    wait: 30000
    一段事件后
    resolve: 30000

    eg. 2

    function sleep(wait) {
      return new Promise((resolve, rej) => {
        console.log(`wait:  ${wait}`);
        setTimeout(() => {
          resolve(wait);
          console.log(`resolve: ${wait}`);
        }, wait);
      });
    }
    function sleep2(wait) {
      return new Promise((resolve, rej) => {
        console.log(`wait:  ${wait}`);
        setTimeout(() => {
          rej(wait);
          console.log(`reject: ${wait}`);
        }, wait);
      });
    }
    async function demo() {
      let result01 = await sleep(100);
      try {
        let result02 = await sleep2(200);
      } catch (error) {
        let result03 = await sleep(300);
      }
    }
    let p = demo().catch((e) => {
      console.log("promise catch", e);
    });
    

    'wait:' 100
    'resolve': 100
    'wait': 200
    'reject': 200
    'wait': 300
    'resolve': 300

    由于错误在内部处理没有作为 Promise 抛出所以 demo().catch() 捕捉不到。

    async 中异步任务

    在 async 中使用 Promise.all([ ]) 生成一个新的 Promise,来达到异步任务效果。

    
    function sleep(wait) {
      return new Promise((resolve, rej) => {
        console.log(`wait:  ${wait}`);
        setTimeout(() => {
          resolve(wait);
          console.log(`resolve: ${wait}`);
        }, wait);
      });
    }
    async function demo() {
      let res = Promise.all([(sleep(1000), sleep(1000), sleep(1000))]);
    }
    let p = demo().catch((e) => {
      console.log("promise catch", e);
    });
    
    

    'wait:' 10000
    'wait:' 10000
    'wait:' 10000
    一段时间后
    resolve: 10000 * 3 (几乎同时)

    现有一些 forEach 或者 map 的循环里,比如在 forEach 里使用 await,这时候的上下文就变成了 array,而不是 async function,就会报错。这时候你就要想到是什么错误。

    参考:https://www.jianshu.com/p/fe0159f8beb4

  • 相关阅读:
    javascript事件列表解说
    如何在ASP.NET页面中嵌入WINFORM控件
    ASP.NET 刷新后如何保持网页的位置
    JS的event 对象
    imp导入数据到ORACLE遭遇ORA12899错误
    求鞍点
    全排列递归实现
    三点顺序
    NYoj 14会场安排问题
    strchr和strstr函数
  • 原文地址:https://www.cnblogs.com/xiaoxu-xmy/p/13819334.html
Copyright © 2020-2023  润新知