zoukankan      html  css  js  c++  java
  • Promise 对象

    Promise 对象用于处理异步请求,保存一个异步操作最终完成(或失败)的结果

    语法

    new Promise(
        /* executor */
        function(resolve, reject) {...}
    );
    
    /*
       来自谷歌翻译
          promise:承诺,约定
          resolve:解决,决定,下决心
          reject:拒绝,驳回,抵制 
    */

    参数:

    promise 构造函数接受一个 executor 函数作为参数,该函数的两个参数分别是 resolve 和 reject,它们是两个函数(executor 函数 在 Promise 构造函数返回新对象之前被调用)。

    resolve 函数被调用时,将 promise 对象从 “未完成” 变为 “成功” (即 pending --> fulfilled)

    reject 函数被调用时,将 promise 对象从 “未完成” 变为 “失败” (即 pending --> rejected)

    描述

    promise 对象是一个代理对象(代理一个值),被代理的值在 promise 对象创建时可能是未知的。它允许你为异步操作的成功和失败分别绑定相应的处理方法,使得异步方法可以像同步方法那样返回值,但不是立即返回最终执行结果,而是一个能代表未来出现的结果的 promise 对象。

    上面提到过,一个 promise 对象有三个状态:

    • pending:初始状态,不是成功或失败状态

    • fulfilled:成功完成

    • rejected:失败

    pending 状态可能触发 fulfilled 状态并传递一个值给相应的状态处理方法,也可能触发 rejected 状态并传递失败信息。当其中任一种情况发生时,promise 对象的 then 方法绑定的处理方法就会被调用。

    (then 方法包含两个参数:onfulfilled 和 onrejected,也都是函数。当 promise 对象的状态为 fulfilled 时调用,调用 then 的 onfulfilled 方法;反之,调用 onrejected 方法。所以异步操作的完成和绑定处理方法之间不存在竞争)

    then 方法的使用语法:

    promise.then(function(value) {
      // onfulfilled
    }, function(error) {   // 第二个参数是可选的,不一定要提供
      // onrejected 
    });

    示例:

        let myFirstPromise = new Promise(function(resolve, reject){
            // 当异步代码执行成功时,调用resolve()方法;失败时,调用reject()方法
            // 此处,使用定时器来模拟异步代码,实际编码可能是XHR请求或HTML5的一些API方法
            setTimeout(function(){
                //resolve('成功!')  //代码执行成功,调用resolve()方法
                resolve('成功!')
            }, 2000)
        })
        
        myFirstPromise.then(function(successMessage){
            // successMessage 是上面调用resolve()方法传入的值
            // successMessage 参数不一定非要是字符串类型,这里只是举个例子
            console.log('Yay!'+successMessage)
        })
    
        console.log('看看我的位置在哪里?')

    运行结果:

    了解Promise的基本用法以后,通过一个小栗子来了解Promise的应用场景

      // 日常开发中,可能会遇到一个函数的参数依赖于其它函数的返回值,如果其它函数的返回值不能先于执行该函数前返回,就不会得到我们预期的结果
      // 此例中,执行add函数时,传入的getValueX方法直接返回2,而getValueY方法存在1秒钟的延时,当前拿不到返回值,最终导致求和失败 
      function add(valueX, valueY) {
        console.log(valueX + valueY)  
      }
    
      add(getValueX(), getValueY())   // NaN  (valueX -> 2, valueY -> undefined)
    
      function getValueX() {
        return 2
      }
    
      // 模仿一个异步请求,1秒后返回数字3
      function getValueY() {
        setTimeout(() => {
          return 3
        }, 1000)
      }
     
    通过 Promise对象来实现类似的求和方法
      function add(promiseX, promiseY) {
        return Promise.all([promiseX, promiseY]).then(values => {
          return values[0] + values[1]
        })
      }
      console.log('2秒以后查看x+y求和的结果')
      add(fetchX(), fetchY()).then(sum => {
        console.log('x + y 的和为:' + sum)      // x + y 的和为:3
      })
    
      function fetchX() {
        return new Promise(resolve => {
          resolve(1)
        }).then(res => {
          return res
        })
      }
    
      function fetchY() {
        return new Promise(resolve => {
          setTimeout(resolve, 2000, 2)
        }).then(res => {
          return res
        })
      }

    对比之下,有了Promise对象,就可以清晰地将异步操作以同步操作的流程表达出来

    注意:

    1、Promise 新建后就会立即执行
      let promise = new Promise((resolve, reject) => {
        console.log('作出承诺:"将来"一定会执行!')
        resolve('resolved')
      })
    
      promise.then(res => {
        console.log(res)
      })
    
      console.log('test')
    
      // 作出承诺:"将来"一定会执行!
      // test
      // resolved
    示例中,Promise 新建后立即执行,输出 "作出承诺:"将来"一定会执行!",当前脚本所有同步任务执行完以后再执行 then 方法
     
    2、Promise 的状态一旦改变,就永久保持该状态,不会再变了
      let promise  = new Promise((resolve, reject) => {
        resolve('ok')
        throw new Error('error')
      })
    
      promise.then(res => console.log(res))
             .catch(err => console.log(err))
      // ok

    此例中,Promise 在 resolve语句后面再抛出错误,由于此时Promise状态已经变成 resolved,再抛出错误就无效了

     
    3、resolve和reject函数的参数会被分别传递给回调函数,reject函数的参数通常是Error对象的实例,表示抛出错误;resolve函数的参数除了正常值以外,还可能是另一个Promise实例
      let p1 = new Promise(reject => {
        reject(new Error('P1 fail... ...'))
      })
      let p2 = new Promise(resolve => {
        resolve(p1)
      })
    
      p2.then(res => console.log(res))
        .catch(err => console.log(err))
      
      // Error: P1 fail... ...

    4、调用 resolve 或 reject 并不会终结 Promise 的参数函数的执行

      new Promise((resolve, reject) => {
        resolve(1)
        console.log(2)
      }).then(res => {
        console.log(res)
      })
    
      // 2
      // 1
    示例中,调用resolve(1)以后,后面的console.log(2)还是执行了,并且会先执行。resolved 回调函数是在本轮事件循环的末尾执行,总是晚于本轮循环的同步任务
    通常,调用 resolve 或 reject以后,Promise的使命就完成了,后续操作应该放在then方法里面,而不要直接写在resolve或reject的后面
      // 在resolve或reject前面加上return
      new Promise((resolve, reject) => {
        return resolve(1)
        console.log(2)
      }).then(res => {
        console.log(res)
      })
      // 1
    
      // 后续操作放在then方法里
      new Promise((resolve, reject) => {
        resolve(2)
      }).then(res => {
        console.log(res)
        console.log(1)
      })
    
      // 2
      // 1

    通过Promise异步加载图片的示例:

       function loadImageAsync(url) {
        return new Promise((resolve, reject) => {
          let image = new Image()
    
          image.onload = function() {
            resolve(url)
          }
    
          image.onerror = function() {
            reject(new Error(`can't find the image: ${url}`))
          }
    
          image.src = url
          document.body.appendChild(image)
    
        })
      }
    
      loadImageAsync('https://www.baidu.com/img/xinshouye_c9d9de2ff40fa160f807f75f34db4ad0.gif')

     Promise.prototype.then()

    它的作用是为 Promise 实例添加状态改变时的回调函数,then 方法的第一个参数是 resolved 状态的回调函数,第二个参数可选,是 rejected 状态的回调函数

      promise.then(function(res) {
        // success    
      }, function(err) {
        // error
      })

    尽管then 方法可以接受第二个参数,但一般不建议在 then 方法里定义第二个参数(rejected 状态的回调函数),建议使用 catch 方法

    Promise.prototype.catch()

    它的作用是指定发生错误时的回调函数,相当于 then 方法的第二个参数 promise.then(null, rejection)

    catch 方法不仅可以处理异步操作抛出的错误,还可以捕获then 方法在运行中抛出的错误。而且在语义结构上更加接近同步的写法(try /catch)

    {
      // 捕获 promise 实例状态从 pending 变为 rejected 的错误
      let promise = new Promise((resolve, reject) => {
        reject(new Error('test error'))
      })
    
      promise.then(res => console.log(res))
              .catch(err => console.log(err))
      // Error: test error
    }
    
    {
      // 捕获 then 方法中运行时抛出的错误
      let promise = new Promise(resolve => {
        resolve(x / 0)
      })
    
      promise.then(res => console.log(res))
              .catch(err => console.log(err))
      // ReferenceError: x is not defined
    }

    Promise.prototype.finally()

    finally 方法用于指定不管 Promise 对象最后状态如何,都会执行的操作。

    finally 方法的回调函数不接受任何参数,因此,也就不知道 Promise 的状态是 fulfield 还是 rejected,表明 finally 方法不依赖于 Promise 的执行结果,与状态无关

      let promise = new Promise((resolve, reject) => {
        if(5 > 3) {
          resolve('计算正确')
        } else{
          reject(new Error('计算出错了'))
        }
      })
    
      promise.then(res => console.log(res))
              .catch(err => console.log(err))
              .finally(() => console.log('不管计算对错,都要执行fianlly'))
    
      // 计算正确
      // 不管计算对错,都要执行fianlly

    Promise.all()

    Promise.all 方法用于将多个 Promise 实例,包装成一个新的 Promise 实例

    const p = Promise.all([p1, p2, p3]);

    all 方法接受一个数组作为参数,参数必须是 Promise 实例

    p 的状态由 p1、p2、p3 决定,分成两种情况

    (1)、p1、p2、p3 的状态都变成 fulfilled,p 的状态才会变成 fulfilled,此时 p1、p2、p3 的返回值组成一个数组,传递给 p 的回调函数

    (2)、p1、p2、p3 之中有一个被 rejected,p 的状态就会变成 rejected,此时第一个被 rejected 的实例的返回值,会传递给 p 的回调函数

    应用场景:要求页面同时加载3张图片,只要有一个加载失败就会报错

      function loadImgAsync(src) {
        return new Promise((resolve, reject) => {
          let img=document.createElement('img');
          img.src=src;
          img.onload=function(){
            resolve(img);
          }
          img.onerror=function(err){
            reject(err);
          }
        })
      }
    
      function showImgs(imgs) {
        imgs.forEach(function(img){
          document.body.appendChild(img);
        })
      }
    
      Promise.all([
        loadImgAsync('http://www.tietuku.com/static/image/icon3.png'),
        loadImgAsync('http://www.tietuku.com/static/image/icon2.png'),
        loadImgAsync('http://www.tietuku.com/static/image/icon1.png')
      ]).then(showImgs)

    Promise.race()

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

    const p = Promise.race([p1, p2, p3]);

    Promise.all 方法看起来像是 逻辑与 的运算,只有数组中的 Promise 实例对象全部执行成功,新的 Promise 实例对象才会执行 resolved 回调函数

    Promise.race 方法则像是 逻辑或 的运算,就像 race 的中文意思:竞赛。只要p1、p2、p3之中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给p的回调函数

    将上例需求稍作更改,只要有一个图片加载成功,即可加载到页面中,这个时候就要把all 改成 race 了

      function loadImgAsync(src) {
        return new Promise((resolve, reject) => {
          let img=document.createElement('img');
          img.src=src;
          img.onload=function(){
            resolve(img);
          }
          img.onerror=function(err){
            reject(err);
          }
        })
      }
    
      function showImgs(img){
        let p=document.createElement('p');
        p.appendChild(img);
        document.body.appendChild(p)
      }
    
      Promise.race([
        loadImgAsync('http://www.tietuku.com/static/image/icon3.png'),
        loadImgAsync('http://www.tietuku.com/static/image/icon2.png'),
        loadImgAsync('http://www.tietuku.com/static/image/icon1.png')
      ]).then(showImgs)

  • 相关阅读:
    smartFoxClinet客户端官方中文Doc
    testTrycatch和catch中的应用程序恢复
    这几天做仿豆丁网flash文档阅读器,百度文库阅读器经验总结
    怎么通过生成动态对象名来调用一个对象?
    AS3的Number类型变量不指定初始值,则其初始值为NaN,而不是0.0
    最适合女生的五大紧缺游戏开发职位
    [阻塞和非阻塞]
    网络游戏客户端的日志输出
    我心目中的MMO
    和某游戏猎头的对话
  • 原文地址:https://www.cnblogs.com/rogerwu/p/7661820.html
Copyright © 2011-2022 走看看