• ES6 入门教程 17 Promise 对象 17.11 Promise.reject() & 17.12 应用 & 17.13 Promise.try()


    ES6 入门教程

    ECMAScript 6 入门

    作者:阮一峰

    本文仅用于学习记录,不存在任何商业用途,如侵删

    17 Promise 对象

    17.11 Promise.reject()

    Promise.reject(reason)方法也会返回一个新的 Promise 实例,该实例的状态为rejected

    const p = Promise.reject('出错了');
    // 等同于
    const p = new Promise((resolve, reject) => reject('出错了'))
    
    p.then(null, function (s) {
      console.log(s)
    });
    // 出错了
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    上面代码生成一个 Promise 对象的实例p,状态为rejected,回调函数会立即执行。

    Promise.reject()方法的参数,会原封不动地作为reject的理由,变成后续方法的参数。

    Promise.reject('出错了')
    .catch(e => {
      console.log(e === '出错了')
    })
    // true
    
    • 1
    • 2
    • 3
    • 4
    • 5

    在这里插入图片描述

    上面代码中,Promise.reject()方法的参数是一个字符串,后面catch()方法的参数e就是这个字符串。

    17.12 应用
    17.12.1 加载图片

    可以将图片的加载写成一个Promise,一旦加载完成,Promise的状态就发生变化。

    const preloadImage = function (path) {
      return new Promise(function (resolve, reject) {
        const image = new Image();
        image.onload  = resolve;
        image.onerror = reject;
        image.src = path;
      });
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    17.12.2 Generator 函数与 Promise 的结合

    使用 Generator 函数管理流程,遇到异步操作的时候,通常返回一个Promise对象。

    function getFoo () {
      return new Promise(function (resolve, reject){
        resolve('foo');
      });
    }
    
    const g = function* () {
      try {
        const foo = yield getFoo();
        console.log(foo);
      } catch (e) {
        console.log(e);
      }
    };
    
    function run (generator) {
      const it = generator();
    
      function go(result) {
        if (result.done) return result.value;
    
        return result.value.then(function (value) {
          return go(it.next(value));
        }, function (error) {
          return go(it.throw(error));
        });
      }
    
      go(it.next());
    }
    
    run(g);
    
    • 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

    上面代码的 Generator 函数g之中,有一个异步操作getFoo,它返回的就是一个Promise对象。函数run用来处理这个Promise对象,并调用下一个next方法。

    17.13 Promise.try()

    实际开发中,经常遇到一种情况:不知道或者不想区分,函数f是同步函数还是异步操作,但是想用 Promise 来处理它。

    因为这样就可以不管f是否包含异步操作,都用then方法指定下一步流程,用catch方法处理f抛出的错误。一般就会采用下面的写法。

    Promise.resolve().then(f)
    
    • 1

    上面的写法有一个缺点,就是如果f是同步函数,那么它会在本轮事件循环的末尾执行。

    const f = () => console.log('now');
    Promise.resolve().then(f);
    console.log('next');
    // next
    // now
    
    • 1
    • 2
    • 3
    • 4
    • 5

    上面代码中,函数f是同步的,但是用 Promise 包装了以后,就变成异步执行了。

    那么有没有一种方法,让同步函数同步执行,异步函数异步执行,并且让它们具有统一的 API 呢?回答是可以的,并且还有两种写法。第一种写法是用async函数来写。

    const f = () => console.log('now');
    (async () => f())();
    console.log('next');
    // now
    // next
    
    • 1
    • 2
    • 3
    • 4
    • 5

    上面代码中,第二行是一个立即执行的匿名函数,会立即执行里面的async函数,因此如果f是同步的,就会得到同步的结果;如果f是异步的,就可以用then指定下一步,就像下面的写法。

    (async () => f())()
    .then(...)
    
    • 1
    • 2

    需要注意的是,async () => f()会吃掉f()抛出的错误。所以,如果想捕获错误,要使用promise.catch方法。

    (async () => f())()
    .then(...)
    .catch(...)
    
    • 1
    • 2
    • 3

    第二种写法是使用new Promise()

    const f = () => console.log('now');
    (
      () => new Promise(
        resolve => resolve(f())
      )
    )();
    console.log('next');
    // now
    // next
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    上面代码也是使用立即执行的匿名函数,执行new Promise()。这种情况下,同步函数也是同步执行的。

    鉴于这是一个很常见的需求,所以现在有一个提案,提供Promise.try方法替代上面的写法。

    const f = () => console.log('now');
    Promise.try(f);
    console.log('next');
    // now
    // next
    
    • 1
    • 2
    • 3
    • 4
    • 5

    事实上,Promise.try存在已久,Promise 库BluebirdQwhen,早就提供了这个方法。

    由于Promise.try为所有操作提供了统一的处理机制,所以如果想用then方法管理流程,最好都用Promise.try包装一下。

    这样有许多好处,其中一点就是可以更好地管理异常。

    function getUsername(userId) {
      return database.users.get({id: userId})
      .then(function(user) {
        return user.name;
      });
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    上面代码中,database.users.get()返回一个 Promise 对象,如果抛出异步错误,可以用catch方法捕获,就像下面这样写。

    database.users.get({id: userId})
    .then(...)
    .catch(...)
    
    • 1
    • 2
    • 3

    但是database.users.get()可能还会抛出同步错误(比如数据库连接错误,具体要看实现方法),这时你就不得不用try...catch去捕获。

    try {
      database.users.get({id: userId})
      .then(...)
      .catch(...)
    } catch (e) {
      // ...
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    上面这样的写法就很笨拙了,这时就可以统一用promise.catch()捕获所有同步和异步的错误。

    Promise.try(() => database.users.get({id: userId}))
      .then(...)
      .catch(...)
    
    • 1
    • 2
    • 3

    事实上,Promise.try就是模拟try代码块,就像promise.catch模拟的是catch代码块。

  • 相关阅读:
    【Vue】轻松理解数据代理
    mmcls多标签分类实战(三):多标签分类指标
    工厂仪表定时拍照智能AI算法识别内网部署方案
    Linux服务器被入侵后的排查思路(应急响应思路)
    腾讯云服务器“可用区”相关问题解答(新手选择指导)
    深入解析MySQL数据库存储数据的方式(上)
    第4章 系统管理员模块功能实现
    基于深度学习视觉算法的多模型文件融合检测系统设计与实现及优化(工人姿态检测+安全帽佩戴检测系统)
    极验深知v2分析
    通过电商API接口分析电商平台客户消费行为
  • 原文地址:https://blog.csdn.net/weixin_44226181/article/details/127957223