最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 异步-Promise

    正文概述 掘金(你不对劲_)   2021-01-18   447

    什么是Promise

    Promise 是一个构造函数,是用来解决JS异步编程(回调地狱)的一种解决方案,怕有些小伙伴不清楚,所以我在本章最后写了案例o.O,下面我们先来了解 Promise 的一些基础知识。

    Promise 特点

    1. 对象的状态不受外界的影响 

      Pending   (进行中)Fulfilled (已成功)Rejected  (已失败)
      
    2. 状态的固化(状态一旦发生改变就不会再改变)

      Pending -> Fulfilled
      Pending -> Rejected
      

      Promise 创建

    • 构造函数接收一个 executor的函数作为参数。该函数的两个参数 resolvereject,它们都是函数,用来改变 Promise 对象的状态。resolve是将Promise对象的状态由‘进行中’ 变为 ‘成功’Pending -> Fulfilled,在异步操作成功时调用,并将成功的结果作为参数传递给对应的回调函数。reject是将Promise对象状态由‘进行中’ 变为 ‘失败’Pending -> Rejected,在异步操作失败时调用,并将失败的信息作为参数传递给对应的回调函数。

    • executor函数什么时候执行呢?

      let promise = new Promise((resolve, reject) => {
        console.log(1);
      })
      console.log(2);
      // 1 2 
      // 在 new 创建 promise 实例时立即调用。
      
    • 在Promise实例生成后,可以通过调用then()方法来绑定异步操作成功/失败的回调函数。then()方法接收两个回调函数作为参数,第一个参数必选,Promise 对象成功状态的回调函数,第二个参数可选,Promise 对象失败状态的回调函数。

      let promise = new Promise((resolve, reject) => {
        setTimeout(() =>{
          Math.random() * 100 > 60 ? resolve('及格') : reject('不及格');
        }, 300)
      })
          
      promise.then((result) => {
        console.log(result);
      }, (reason) => {
        console.log(reason);
      })
      
    • 执行顺序,我们发现输出的顺序是 1 -> 3 -> 2,Promise 新建后立即执行,所以先输出 1,然后通过resolve改变Promise对象的状态来调用then()方法中对应的回调函数是异步操作,所以会等待当前脚本所有的同步任务执行完成后才执行,所以先打印3,最后才是2。

      let promise = new Promise((resolve,reject) => {
        console.log(1);
        resolve(2);
      })
          
      promise.then((res) => {
        console.log(res);
      })
          
      console.log(3);
          
      // 1 3 2
      

    与定时器异步的区别

    • 首先先简单提一下JS代码的执行过程:同步的进入主线程,异步会的进入Event Table并注册函数。当指定的事情完成时,Event Table会将这个函数移入Event Queue。主线程内的任务执行完毕为空,会去Event Queue读取对应的函数,进入主线程执行。上述过程会不断重复,也就是常说的Event Loop(事件循环)。

    • JS 异步代码中,分为宏任务与微任务,他们分别有自己的任务队列。微任务:promise、process.nextTick();宏任务:setTimeout、setInterval, Ajax,DOM事件。这里就存在一个优先级的问题,当主线程空闲时间,会先检查微任务队列,然后再走宏任务队列。所以下面的代码执行顺序就是 2 -> 4 -> 3 -> 1

      setTimeout(() => {
        console.log(1);
      }, 30)
      let promise = new Promise((resolve, reject) => {
        console.log(2);
        resolve(3);
      })
      promise.then((res) => {
        console.log(res);
      })
      console.log(4);
      

    then方法链式调用

    • then()方法的作用是为 Promise 实例添加成功和失败状态的回到函数,会返回一个新的 Promise 实例,所以then()方法后面可以继续跟另一个then()方法进行链式调用。

      let promise = new Promise((resolve, reject) => {
        resolve(1);
      })
      promise.then((result) => {
        console.log(result);
        return 2;  
      }, (reason) => {
        console.log(reason);
      })
      .then((result) => {  // 上一次then的返回值作为这次的参数,没有则为 undefined
        console.log(`then2: ${result}`);
      })
      
    • 如果前一个then()方法中的回调返回的是一个 Promise 实例,这时下一个then()方法中的回调会根据前一个的 Promise 实例的状态来进行调用。

      let p = new Promise((resolve, reject) => {
        resolve('success');
      })
      p.then((result) => {
        console.log(result); // -> 'success'
        return new Promise((resolve, reject) => {
          reject(`error-.-`);
        })
      })
        .then((result) => {
          console.log(result); // -> 不走这里因为上一次返回的 Promise 状态为 Rejected
        }, (reason) => {
          console.log(reason); // -> 'error-.-'
        })
      

    catch方法

    • 用于指定发生错误时的回调函数,就等同于then()方法的第二个回调函数。

      let promise = new Promise((resolve, reject) => {
        resolve(data);
      })
      // 因为then方法第一个参数是必选项,又用不到所以就填一个null
      promise.then(null, (reason) => {
        console.log(reason);
      })
      // === 
      promise.then((res) => {
        console.log(res);
      })
        .catch((err) => {
        console.log(err);
      })
      

    状态固化

    • Promise 对象状态一旦发生改变,就不会再变。

      let promise = new Promise((resolve, reject) => {
        resolve('Fulfilled');  // 将状态改为 Fulfilled
        console.log(data);  // 再抛出错误也不会改变状态
      })
          
      promise.then(res => console.log(res)) 
             .catch(err => console.log(err)); // 捕获不到
      // -> Fulfilled
      

    状态依赖

    • 当前 p2 的状态依赖于 p1 的状态,就会导致自身的状态失效。

      let p1 = new Promise((resolve, reject) => {
        setTimeout(() => {
          reject('fail')
        }, 1000)
      })
          
      let p2 = new Promise((resolve, reject) => {
        setTimeout(() => {
          resolve(p1)
        }, 500)
      })
          
      p2.then(res => console.log(res))
        .catch(err => console.log(err)); // -> fail
      

    all和race

    • 都是 Promise 构造器上的方法,Promise.all(),可以将多个 Promise 实例包装成一个新的Promise 实例。成功与失败的返回值是不同的,当所有的都成功时返回的结果是一个数组,失败时返回最先触发失败状态的错误信息。

    • 注意: Promise.all() 获取的成功结果的数组里的数据与传入时的顺序是一致的。

      let promise1 = new Promise((resolve, reject) => {
        setTimeout(() => {
          resolve('peace');
        }, 1000)
      })
          
      let promise2 = new Promise((resolve, reject) => {
        setTimeout(() => {
          resolve('and');
        }, 2000)})
          
      let promise3 = new Promise((resolve, reject) => {
        setTimeout(() => {
          resolve('love');
        }, 300)
      })
          
      let p = Promise.all([promise1, promise2, promise3]);
          
      console.log(p);
      p.then(res => console.log(res)) // -> ["peace", "and", "love"]
       .catch(err => console.log(`reject: ${err}`));
          
      
    • Promise.race(),与Promise.all()的用法相同,但是功能不同,它是获取最快返回结果的那一个,无论结果本身是成功态还是失败状态。

      let promise1 = new Promise((resolve, reject) => {
        setTimeout(() => {
          resolve('peace');
        }, 1000)
      })
          
      let promise2 = new Promise((resolve, reject) => {
        setTimeout(() => {
          resolve('and');
        }, 2000)})
          
      let promise3 = new Promise((resolve, reject) => {
        setTimeout(() => {
          resolve('love');
        }, 300)
      })
          
      let p = Promise.race([promise1, promise2, promise3]);
          
      console.log(p);
      p.then(res => console.log(res)) // -> love
       .catch(err => console.log(`reject: ${err}`));
      

    解决回调地狱问题

    // 在无 Promise 之前  node环境读取文件
    const fs = require('fs');
    
    // 这种代码非常的不好维护
    fs.readFile('./name.txt', 'utf-8', (err, data) => {
      if(data){
        fs.readFile(data, 'utf-8', (err, data) => {
          if(data){
            fs.readFile(data, 'utf-8', (err, data) => {
              console.log(data);
            })
          }
        })
      }
    })
    
    // Promise化管理
    function readFile (path) {
      return new Promise((resolve, reject) => {
        fs.readFile(path, 'utf-8', (err, data) => {
          if(data){
            resolve(data);
          }
        })
      })
    }
    
    readFile('./name.txt').then(data => return readFile(data))
                          .then(data => return readFile(data))
                          .then(data => console.log(data))
    
    // 将传入的函数Promise化
    function promisify (fn) {
      return function (...args) {
        return new Promise((resolve, reject) => {
          fn(...args, (err, data) => {
            if(err){
              reject(err);
            }else{
              resolve(data);
            }
          })
        })
      }
    }
    

    起源地下载网 » 异步-Promise

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元