最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 更好理解Promise日常使用从手写PromiseA+开始

    正文概述 掘金(奔跑鸭)   2021-03-29   523

    Promise

    目的:为了更好使用Promise日常使用。

    Promise概念

    Promise是JavaScript的内置对象,同时也是一个构造函数
    特别说明:Promise构造函数是为了解决异步问题;同步代码中也可以使用(大材小用)。

    作为内置对象

    静态方法

    • Promise.all(iterable)
    • Promise.allSettled(iterable)
    • Promise.any(iterable)
    • Promise.race(iterable)
    • Promise.reject(reason)
    • Promise.resolve(value)

    作为构造函数

    Promise.prototype

    • Promise.prototype.constructor
    • Promise.prototype.then(onFulfilled, onRejected)
      • onFulfilled为可选参数
      • onRejected为可选参数
    • Promise.prototype.catch(onRejected)
      • onRejected为可选参数
    • Promise.prototype.finally(onFinally)
      • onFinally为可选参数

    Promise实例

    const promise = new Promise(function(resolve, reject) {
      // ...省略代码
      if (/* 异步操作成功 */){
        resolve(value);
      } else {
        reject(error);
      }
    });
    
    关于executor函数、resolve函数、reject函数的说明
    • Promise构造函数接受一个executor函数作为参数
    • executor函数的两个参数分别是resolve函数和reject函数
      • resolve函数的作用是,将Promise对象的状态从“未完成”变为“成功”(即从 pending 变为 resolved),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;
      • reject函数的作用是,将Promise对象的状态从“未完成”变为“失败”(即从 pending 变为 rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。
    promise.then(function(value) {
      // success
    }, function(error) {
      // failure
    });
    
    关于then方法说明
    • Promise实例生成以后,可以用then方法分别指定fulfilled状态和rejected状态的回调函数。
    • then方法可以接受两个参数。
      • 参数可选:onFulfilled 和 onRejected 都是可选参数,不一定要提供。
      • 如果 onFulfilled 不是函数,其必须被忽略
      • 如果 onRejected 不是函数,其必须被忽略
      • 如果 onFulfilled 是函数:
        • 当 promise 执行结束后其必须被调用,其第一个参数为 promise 的终值value
        • 在 promise 执行结束前其不可被调用
        • 其调用次数不可超过一次
      • 如果 onRejected 是函数:
        • 当 promise 被拒绝执行后其必须被调用,其第一个参数为 promise 的据因reason
        • 在 promise 被拒绝执行前其不可被调用
        • 其调用次数不可超过一次
    • then方法返回的是一个新的Promise实例(注意,不是原来那个Promise实例)。因此可以采用链式写法,即then方法后面再调用另一个then方法。

    手写Promise构造函数更好理解Promise(PromiseA+标准)

    特别说明:通过类实现Promise构造函数

    1. 实现executor函数、resolve函数、reject函数

    class Promise{
      // 构造器
      constructor(executor){
        // 成功
        let resolve = () => { };
        // 失败
        let reject = () => { };
        // 立即执行executor函数
        executor(resolve, reject);
      }
    }
    

    2.实现Promise构造函数基本状态(state)

    • Promise构造函数三种状态
      • pending(进行中)
      • fulfilled(已成功)
      • rejected(已失败)
    • Promise构造函数状态切换
      • pending(等待态)为初始态
      • 通过resolve函数可以转化为fulfilled(成功态)
        • 成功时,不可转为其他状态,且必须有一个不可改变的值(value)
      • 通过reject函数可以转化为rejected(失败态)
        • 失败时,不可转为其他状态,且必须有一个不可改变的原因(reason)
      • 若是executor函数报错 直接执行reject函数
    • Promise构造函数几个关键属性
      • state: 状态描述
      • value: 成功值
      • reason: 失败原因
    class Promise{
      constructor(executor){
        // 初始化state为等待态(state状态只能改变一次)
        // 因为只有状态为pending时候可以改变
        this.state = 'pending';
        // 成功初始值
        this.value = undefined;
        // 失败原因初始值
        this.reason = undefined;
        
        // resolve函数、reject函数
        const resolve = (value) => {
          // 如果state不为pending,下面条件为false,块级作用域代码不再执行
          if (this.state === 'pending') {
            // resolve调用后,state转化为fulfilled成功态
            this.state = 'fulfilled';
            // 更新成功的值
            this.value = value;
          }
        };
        const reject = (reason) => {
          // 如果state不为pending,下面条件为false,块级作用域代码不再执行
          if (this.state === 'pending') {
            // reject调用后,state转化为rejected失败态
            this.state = 'rejected';
            // 更新失败的原因
            this.reason = reason;
          }
        };
        
        // 如果executor执行报错,直接执行reject函数,错误信息作为入参传给reject函数
        try{
          executor(resolve, reject);
        } catch (error) {
          reject(error);
        }
      }
    }
    

    3.实现Promise实例的then方法(Promise.prototype.then)

    • then的方法接收两个参数(可选):onFulfilled函数和onRejected函数
    • onFulfilled函数的参数为成功值value
    • onRejected函数的参数为失败原因reason

    executor函数同步执行

    class Promise{
      constructor(executor){ 
          // ...省略代码
      }
      // then方法有两个参数onFulfilled函数和onRejected函数
      then(onFulfilled,onRejected) {
        // 状态为fulfilled,执行onFulfilled函数,传入成功的值
        if (this.state === 'fulfilled') {
          onFulfilled(this.value);
        };
        // 状态为rejected,执行onRejected函数,传入失败的原因
        if (this.state === 'rejected') {
          onRejected(this.reason);
        };
      }
    }
    

    executor函数在异步中调用resolve函数或reject函数

    class Promise{
      constructor(executor){
        this.state = 'pending';
        this.value = undefined;
        this.reason = undefined;
        // onFulfilled函数回调数组
        this.onResolvedCallbacks = [];
        // onRejected函数回调数组
        this.onRejectedCallbacks = [];
        const resolve = (value) => {
          if (this.state === 'pending') {
            this.state = 'fulfilled';
            this.value = value;
            // 一旦resolve执行,调用onFulfilled函数回调数组
            this.onResolvedCallbacks.forEach(fn=>fn());
          }
        };
        const reject = (reason) => {
          if (this.state === 'pending') {
            this.state = 'rejected';
            this.reason = reason;
            // 一旦reject执行,onRejected函数回调数组
            this.onRejectedCallbacks.forEach(fn=>fn());
          }
        };
        try{
          executor(resolve, reject);
        } catch (err) {
          reject(err);
        }
      }
      then(onFulfilled,onRejected) {
        if (this.state === 'fulfilled') {
          onFulfilled(this.value);
        };
        if (this.state === 'rejected') {
          onRejected(this.reason);
        };
        // 当状态state为pending时
        if (this.state === 'pending') {
          // onFulfilled函数传入onFulfilled函数回调数组
          this.onResolvedCallbacks.push(()=>{
            onFulfilled(this.value);
          })
          // onRejected传入onRejected函数回调数组
          this.onRejectedCallbacks.push(()=>{
            onRejected(this.reason);
          })
        }
      }
    }
    
    • 上述代码同时实现了同一个promise实例被多个then调用问题

      // 多个then的情况
      const p = new Promise();
      p.then();
      p.then();
      

    4.实现Promise实例的链式调用

    调用Promise构造函数原型对象的then、catch、finally都返回新的promise实例

    class Promise{
      constructor(executor){
        // ...省略代码
      }
      then(onFulfilled,onRejected) {
        // 返回的promise2
        const promise2 = new Promise((resolve, reject)=>{
          if (this.state === 'fulfilled') {
            const x = onFulfilled(this.value);
            // resolvePromise函数,处理onFulfilled函数return的x和默认的promise2的关系
            resolvePromise(promise2, x, resolve, reject);
          };
          if (this.state === 'rejected') {
            const x = onRejected(this.reason);
            // resolvePromise函数,处理onRejected函数return的x和默认的promise2的关系
            resolvePromise(promise2, x, resolve, reject);
          };
          if (this.state === 'pending') {
            this.onResolvedCallbacks.push(()=>{
              const x = onFulfilled(this.value);
              resolvePromise(promise2, x, resolve, reject);
            })
            this.onRejectedCallbacks.push(()=>{
              const x = onRejected(this.reason);
              resolvePromise(promise2, x, resolve, reject);
            })
          }
        });
        // 返回promise,完成链式
        return promise2;
      }
    }
    
    resolvePromise函数实现
    1. 如果promisex 指向同一对象(x === promise2),会造成循环引用,自己等待自己完成,则报“循环引用”错误以 TypeError 为据因拒绝执行 promise
    const p = new Promise(resolve => {
      resolve('success');
    });
    const p1 = p.then(data => {
      // 循环引用,自己等待自己完成,一辈子完不成
      return p1;
    })
    
    1. 比较x和promise2,避免循环应用
    2. 如果 x== nulltypeof x !== 'object'typeof x !== 'function'直接resolv(x)
    3. 如果x != null && (typeof x === 'object' || typeof x === 'function')
      • x不是promise,直接resolv(x)
      • x是promise直接调用then方法
    function resolvePromise(promise2, x, resolve, reject){
      // 循环引用报错
      if(x === promise2){
        // reject报错
        return reject(new TypeError('Chaining cycle detected for promise'));
      }
      // 防止多次调用
      let called;
      // x不是null 且x是对象或者函数
      if (x != null && (typeof x === 'object' || typeof x === 'function')) {
        try {
          // A+规定,声明then = x的then方法
          const then = x.then;
          // 如果then是函数,就默认是promise了
          if (typeof then === 'function') { 
            // 就让then执行第一个参数是this后面是成功的回调和失败的回调
            // then方法中this指向x
            then.call(x, y => {
              // 成功和失败只能调用一个
              if (called) return;
              called = true;
              // resolve的结果依旧是promise 那就继续解析
              resolvePromise(promise2, y, resolve, reject);
            }, err => {
              // 成功和失败只能调用一个
              if (called) return;
              called = true;
              reject(err);// 失败了就直接执行reject函数
            })
          } else {
            resolve(x); // 直接成功即可
          }
        } catch (e) {
          // 也属于失败
          if (called) return;
          called = true;
          // 取then出错了那就不要在继续执行了
          reject(e); 
        }
      } else {
        resolve(x);
      }
    }
    

    5.完善then方法中onFulfilled函数和onRejected函数

    • 1、PromiseA+规定onFulfilled,onRejected都是可选参数,如果他们不是函数,必须被忽略
      • onFulfilled是一个普通的值,成功时直接等于 value => value
      • onRejected返回一个普通的值,失败时如果直接等于 value => value,则会跑到下一个then中的onFulfilled中,所以直接扔出一个错误reason => throw err
    • 4、PromiseA+规定onFulfilled或onRejected不能同步被调用,必须异步调用。我们就用setTimeout解决异步问题
    • 5、如果onFulfilled或onRejected报错,则直接返回reject()
    class Promise{
      constructor(executor){
          // ...省略代码
      }
      then(onFulfilled,onRejected) {
        // onFulfilled如果不是函数,就忽略onFulfilled,直接返回value
        onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
        // onRejected如果不是函数,就忽略onRejected,直接扔出错误
        onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err };
        let promise2 = new Promise((resolve, reject) => {
          if (this.state === 'fulfilled') {
            // 异步
            setTimeout(() => {
              try {
                let x = onFulfilled(this.value);
                resolvePromise(promise2, x, resolve, reject);
              } catch (e) {
                reject(e);
              }
            }, 0);
          };
          if (this.state === 'rejected') {
            // 异步
            setTimeout(() => {
              // 如果报错
              try {
                let x = onRejected(this.reason);
                resolvePromise(promise2, x, resolve, reject);
              } catch (e) {
                reject(e);
              }
            }, 0);
          };
          if (this.state === 'pending') {
            this.onResolvedCallbacks.push(() => {
              // 异步
              setTimeout(() => {
                try {
                  let x = onFulfilled(this.value);
                  resolvePromise(promise2, x, resolve, reject);
                } catch (e) {
                  reject(e);
                }
              }, 0);
            });
            this.onRejectedCallbacks.push(() => {
              // 异步
              setTimeout(() => {
                try {
                  let x = onRejected(this.reason);
                  resolvePromise(promise2, x, resolve, reject);
                } catch (e) {
                  reject(e);
                }
              }, 0)
            });
          };
        });
        // 返回promise,完成链式
        return promise2;
      }
    }
    

    6.实现catch和resolve、reject、race、all方法

    class Promise{
      constructor(executor){
        this.state = 'pending';
        this.value = undefined;
        this.reason = undefined;
        this.onResolvedCallbacks = [];
        this.onRejectedCallbacks = [];
        let resolve = value => {
          if (this.state === 'pending') {
            this.state = 'fulfilled';
            this.value = value;
            this.onResolvedCallbacks.forEach(fn=>fn());
          }
        };
        let reject = reason => {
          if (this.state === 'pending') {
            this.state = 'rejected';
            this.reason = reason;
            this.onRejectedCallbacks.forEach(fn=>fn());
          }
        };
        try{
          executor(resolve, reject);
        } catch (err) {
          reject(err);
        }
      }
      then(onFulfilled,onRejected) {
        onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
        onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err };
        let promise2 = new Promise((resolve, reject) => {
          if (this.state === 'fulfilled') {
            setTimeout(() => {
              try {
                const x = onFulfilled(this.value);
                resolvePromise(promise2, x, resolve, reject);
              } catch (e) {
                reject(e);
              }
            }, 0);
          };
          if (this.state === 'rejected') {
            setTimeout(() => {
              try {
                const x = onRejected(this.reason);
                resolvePromise(promise2, x, resolve, reject);
              } catch (e) {
                reject(e);
              }
            }, 0);
          };
          if (this.state === 'pending') {
            this.onResolvedCallbacks.push(() => {
              setTimeout(() => {
                try {
                  const x = onFulfilled(this.value);
                  resolvePromise(promise2, x, resolve, reject);
                } catch (e) {
                  reject(e);
                }
              }, 0);
            });
            this.onRejectedCallbacks.push(() => {
              setTimeout(() => {
                try {
                  const x = onRejected(this.reason);
                  resolvePromise(promise2, x, resolve, reject);
                } catch (e) {
                  reject(e);
                }
              }, 0)
            });
          };
        });
        return promise2;
      }
      // catch方法
      catch(fn){
        return this.then(null,fn);
      }
    }
    function resolvePromise(promise2, x, resolve, reject){
      if(x === promise2){
        return reject(new TypeError('Chaining cycle detected for promise'));
      }
      let called;
      if (x != null && (typeof x === 'object' || typeof x === 'function')) {
        try {
          let then = x.then;
          if (typeof then === 'function') { 
            then.call(x, y => {
              if(called)return;
              called = true;
              resolvePromise(promise2, y, resolve, reject);
            }, err => {
              if(called)return;
              called = true;
              reject(err);
            })
          } else {
            resolve(x);
          }
        } catch (e) {
          if(called)return;
          called = true;
          reject(e); 
        }
      } else {
        resolve(x);
      }
    }
    //resolve方法
    Promise.resolve = function(val){
      return new Promise((resolve,reject)=>{
        resolve(val)
      });
    }
    //reject方法
    Promise.reject = function(val){
      return new Promise((resolve,reject)=>{
        reject(val)
      });
    }
    //race方法 
    Promise.race = function(promises){
      return new Promise((resolve,reject)=>{
        for(let i=0;i<promises.length;i++){
          promises[i].then(resolve,reject)
        };
      })
    }
    //all方法(获取所有的promise,都执行then,把结果放到数组,一起返回)
    Promise.all = function(promises){
      let arr = [];
      let i = 0;
      function processData(index,data){
        arr[index] = data;
        i++;
        if(i == promises.length){
          resolve(arr);
        };
      };
      return new Promise((resolve,reject)=>{
        for(let i=0;i<promises.length;i++){
          promises[i].then(data=>{
            processData(i,data);
          },reject);
        };
      });
    }
    

    Promise配合async/await使用

    let result = true;
    // 从服务器接口获取数据
    async function getServerData(){
        // 模拟请求
        const p = new Promise((resolve,reject)=>{
            setTimeout(() => {
                if(result){
                    resolve('data')
                }else {
                    reject('err')
                }
            }, 1000);
        })
        return p
    }
    // 供前端调用接口
    async function dataController() {
        try {
           const data = await getServerData();
           console.log('data',data);
        } catch (error) {
            // 可以捕获reject
            console.log('error', error);
        }
    }
    // 前端接口
    dataController()
    
    • 特别说明try...catchcatch可以捕获reject函数的reason

    参考文章

    BAT前端经典面试问题:史上最最最详细的手写Promise教程

    关于Generator函数


    起源地下载网 » 更好理解Promise日常使用从手写PromiseA+开始

    常见问题FAQ

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

    发表评论

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

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

    联系作者

    请选择支付方式

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