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

    正文概述 掘金(wyh奔跑吧)   2021-01-26   429

    Promise是什么?

    抽象表达:

    Promise是JS中进行异步编程的新方案(旧方案是回调地狱)。

    具体表达:

    (1)、从语法上来说,Promise是一个构造函数。

    (2)、从功能上来说,Promise是用来封装一个异步操作并可以获取其结果的对象。

    为什么要用Promise?

    (1)、指定回调函数的方式更加灵活:旧的方案必须在启动异步任务前指定回调函数。

    • Promise可以先启动异步任务,返回Promise对象后,再给Promise对象绑定回调函数。
    • Promise甚至可以在异步任务结束后指定。(得到一个Promise对象说明异步任务启动了,启动很快,但是完成需要时间)

    (2)、支持链式调用,可以解决回调地狱的问题:回调地狱就是回调函数嵌套调用,外部回调函数异步执行的结果是嵌套的回调函数执行的条件,这样的写法不便于阅读,也不便于异常处理。

    Promise的状态改变

    Promise改变状态只能从pending变为resolved或者从pending变为rejected这两种,并且只能改一次,无论成功还是失败,都会返回一个结果。成功的结果数据称为value,失败的结果数据称为reason。

    改变promise的状态和指定回调函数谁先谁后?

    都有可能,正常情况下是先指定回调再改变状态,但也可先改变状态再指定回调,只有状态改变,回调函数才会执行。那么什么时候才能的得到数据?

    ①如果先指定的回调,那当状态发生改变时,回调函数就会调用,得到数据

    ②如果先改变的状态,那当指定回调时,回调函数就会调用,得到数据

    先改状态再指定回调函数

    const p = new Promise((resolve,reject) => {
            setTimeout(() => {
                console.log('定时器执行')
                resolve(Date.now())
            }, 1000);
    })
    setTimeout(() => {
          p.then(value => {
               console.log('成功的回调',value)
          })
    }, 2000);
        //打印结果: 
            //定时器执行 
           //成功的回调 1591627205718
          //改状态是在1秒后,指定回调函数延迟更长的时间
    

    先指定回调函数再改状态

    const p = new Promise((resolve,reject) => {
            setTimeout(() => {
                console.log('定时器执行')
                resolve(Date.now())//后改变状态(同时指定数据),然后异步执行回调函数
            }, 1000);
    })
     p.then(value => { //先指定回调函数,但是resolve未执行,状态还没改变,所以回调函数会保存起来
           console.log('成功的回调',value)
     })
        //打印结果: 
           //定时器执行 
          //成功的回调 1591627539711
    

    Promise的同步和异步

    1. promise中的excutor函数:执行器:(resolve,reject) => {},会在Promise内部立即同步回调,执行器中异步任务被放入宏/微队列中等待执行。
    2. .then()也是同步的,.then()和回调函数不是一回事,
    • .then()是去指定回调函数的。同步改的状态,同步指定的回调函数,那么执行回调函数的条件就满足了,按理来说我们应该马上执行回调函数。但是.then()中的回调函数(value=>{},reason=>{}),是异步的,即使条件已经满足也不是立即执行的。
    • .then()中的同步操作是直接在.then()里面return里执行(也就是value=>{}这个大括号里面执行),异步操作就return一个新的Promise(promise封装异步操作)。

    promise.then()返回的新promise的结果由什么决定?

    (1)、简单描述:由then()指定的回调函数决定

    (2)、详细描述:

    a.如果PromiseA返回的是另一个新的PromiseB,PromiseB的结果就是PromiseA的结果
    b.如果PromiseA返回是非Promise的任意值,PromiseA变为resolved,value为返回值。
    c.如果抛出异常,PromiseA变为rejected,reason为抛出异常的原因。
    

    注意事项:下一个then的结果,和上一个Promise执行value=>{}成功的回调还是reason()=>失败的回调没有关系,而是由value()=>{}或reason()=>{}执行的结果决定。

      new Promise((resolve,reject) => {
                reject(1)
            }).then(
                value => console.log('成功的1',value),
                reason => {
                    console.log('失败的1',reason)
                    return new Promise((resolve,reject) => {
                      resolve(2)
                    })
                }
            ).then(
               value => {
                console.log('成功的2',value)
                return 3
               },
               reason => console.log('失败的2',reason)
            ).then(
               value=> {
                   console.log('成功的3',value)
                   throw Error('Error')
               },
               reason=> console.log('失败的3',reason)
            ).then(
                value => console.log('成功的4',value),
                reason => console.log('失败的4',reason)
            )
            //结果:
              失败的1 1
              成功的2 2
              成功的3 3
              失败的4 Error: Error
    

    以上代码说明:

    Promise如何串连多个任务?

    Promise异常传透(穿透)?

    具体点:比如说你中上一个Promise中执行了reject(1),但是后面的then()中并没有写对失败处理的回调(其实相当于写了reason =>{throw reason}才可以把错误向下抛),就会通过.then()一层一层传透过去,直到最后没找到处理失败的回调就会报错:Uncaught (in promise) 1。但是如果你在链式调用中写了.catch(),那么失败的回调就会在这里被捕获,不再往下传透。

    注意:then 和 catch 期望接收函数做参数,如果非函数就会发生 Promise 穿透现象,打印的是上一个 Promise 的返回。

    new Promise((resolve,reject) => {
                reject(1)
            }).then(
                value => console.log('成功的1',value),
            ).catch(
               reason => {
                console.log('reason',reason)
                // throw reason   // ------>在这里如果不抛异常或者返回失败的Promise,后面的then()就会进入成功回调
                // return Promise.reject(reason)
              }
            ).then(
               value => console.log('成功的1',value),
               reason => console.log('失败的1',reason)
            )
          // 结果:reason 1
         //       成功的1 undefined
    

    中断Promise链?

    上面代码中,在catch()里面如果不抛出异常或者返回失败的Promise,后面的then会进入成功的回调,如果抛了异常或返回了失败的Promise,后面的then就会进入失败的回调。如果不想再进入后面的then呢?该怎么办?

    解决办法:返回一个pending状态的Promise。 return new Promise(()=>{})

    这样为什么后面的就不执行了呢?因为一旦返回新的Promise,这个新的Promise的结果就决定了后面的then执行成功或者失败的回调。但是这个新的Promise一直处于pending阶段,无法获取结果。实现了中断Promise链。

    简述Promise A+规范

    术语:

    要求:

    • promise状态
    • then方法
    • promise解决过程

    promise解决过程是一个抽象的操作,其需输入一个promise和一个值,表示为[[Resolve]] (promise,x) 这句话意思:把promise resolvel同时传入x作为值。

    promise.then(function(x) {
    	console.log('会执行这个函数,同时传入x变量的值',x);
    })
    

    手写Promise

    (function() {
        const PENDING = 'pending';
        const RESOLVED = 'resolved';
        const REJECTED = 'rejected';
    
        function Promise(excutor) {
            this.status = PENDING;
            this.data = undefined;
            this.callbacks = [];
            const self = this;
    
            function resolve(value) {
                if(self.status !== PENDING) return;
                this.status = RESOLVED;
                this.data = value;
                if(self.callbacks.length > 0) {
                    setTimeout(() => {
                     self.callbacks.forEach(callbackObj => {
                         callbackObj.onResolved(value)
                     })   
                    });
                }
            }
            
            function reject(reason) {
                if(self.status !== PENDING) return;
                this.status = REJECTED;
                this.data = reason;
                if(self.callbacks.length > 0) {
                    self.callbacks.forEach(callbackObj => {
                        callbackObj.onRejected(reason)
                    })
                }
            }
    
            try {
                excutor(resolve, reject);
            } catch (error) {
                reject(error)
            }
        }
    
        Promise.resolve = function(value) {
            return new Promise((resolve, reject) => {
                if(value instanceof Promise) {
                    value.then(resolve, reject);
                } else {
                    resolve(value);
                }
            })
        }
    
        Promise.reject = function(reason) {
            return new Promise((resolve, reject) => {
                reject(reason);
            })
        }
    
        Promise.all = function(promises) {
            const values = new Array(promises.length); // 指定数组长度
            let count = 0;
            return new Promise((resolve, reject) => {
                promises.forEach((p,index) => {
                    p.then(
                        value => {
                            count++;
                            values[index] = value;
                            if(count === promises.length) {
                                resolve(values);
                            }
                        },
                        reason => {
                            reject(reason)
                        }
                    )
                })
            })
        }
    
        Promise.race = function(promises) {
            return new Promise((resolve, reject) => {
                promises.forEach((p, index) => {
                    p.then(
                        value => {
                            resolve(value);
                        },
                        reason => {
                            reject(reason);
                        }
                    )
                })
            })
        }
    
        Promise.resolveDelay = function(value, delay) {
            return new Promise((resolve, reject) => {
                setTimeout(() => {
                    if(value instanceof Promise) {
                        value.then(resolve, reject);
                    } else {
                        resolve(value);
                    }
                }, delay);
            })
        }
    
        Promise.rejectDelay = function(reason, delay) {
            return new Promise((resolve, reject) => {
                setTimeout(() => {
                    reject(reason)
                }, delay);
            })
        }
        /**
         * 将执行回调函数抽取成一个函数,需要改变then返回的promise的状态,结果有三种情况
         * 1:抛出异常,我们用try...catch捕获,
         * 2:如果返回了一个promise(判断结果是不是promise的实例,调用then去执行
         * 3:如果返回非promise,值就是result
       
         * 如果是pending状态,说明先指定的回调,将回调函数加入队列稍后执行
         * 如果是resolve状态或者rejected,说明状态改变,异步执行队列的函数,
         */
        Promise.prototype.then((onResolved, onRejected) => {
            onResolved = typeof onResolved === 'function' ? onResolved : value => value
            onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason }
            const self = this;
            return new Promise((resolve, reject) => {
             // 提取handle函数,用于改变新的promise的状态
                function handle(callback) {
                    try {
                        const result = callback(self.data);
                        if(result instanceof Promise) {
                            result.then(resolve, reject)
                        } else {
                            resolve(result);
                        }
                    } catch (error) {
                        reject(error)
                    }
                }
                if(self.status === PENDING) {
                    self.callbacks.push({ // 存回调,改状态,
                        onResolved() {
                            handle(onResolved);
                        },
                        onRejected() {
                            handle(onRejected);
                        }
                    })
                } else if(self.status === RESOLVED) {
                    setTimeout(() => {
                        // 异步执行回调函数
                       handle(onResolved)
                    });
                } else {
                    setTimeout(() => {
                        handle(onRejected)
                    });
                }
            })
        })
        window.Promise = Promise;
    })(window);
    

    最后来几道常见面试题吧

    题1:下面三个有哪些不同

    const promise = new Promise(function(resolve, reject){
        resolve(1);
    })
    
    // 1
    promise.then(() => {
      return Promise.resolve(2);
    }).then((n) => {
      console.log(n)
    });
    
    // 2
    promise.then(() => {
      return 2
    }).then((n) => {
      console.log(n)
    });
    
    // 3
    promise.then(2).then((n) => {
      console.log(n)
    });
    // 结果:2 1 2
    分析:1:第一个then里面返回一个新的promise,后面的then在下一个事件循环机制才会去执行
         2:return 一个2,它不用等下一个事件循环。
         3:then 和 catch 期望接收函数做参数,若非函数会发生 Promise 穿透,打印上一个 Promise 的返回。
    

    题2:

    let a;
    const b = new Promise((resolve, reject) => {
      console.log('promise1');
      resolve();
    }).then(() => {
      console.log('promise2');
    }).then(() => {
      console.log('promise3');
    }).then(() => {
      console.log('promise4');
    });
    
    a = new Promise(async (resolve, reject) => {
      console.log(a);
      await b;
      console.log(a);
      console.log('after1');
      await a
      resolve(true);
      console.log('after2');
    });
    
    console.log('end');
    // 结果:promise1, undefined, end, promise2, promise3, promise4, Promise:{<pending>}, after1
    分析:前面的比较简单就不解释了,有一点比较难理解的是:从 await a 开始,a 是必须等待 Promise 的状态改变才会继续往下执行,可 a 的状态是一直得不到更改的,所以无法执行下面的逻辑。只要在 await a 上面加一行 resolve() 就能让后面的 after 2 得到输出。
    

    题3:如何取消promise?

    function wrap(p) {
      let resol = null;
      let abort = null;
      
      const p1 = new Promise((resolve, reject) => {
        resol = resolve;
        abort = reject;
      });
      p1.abort = abort;
      p.then(resol, abort);
      return p1;
    }
    
    const newPromise = wrap(promise);
    newPromise.then(res => console.log)
    newPromise.abort()
    分析:promise 缺陷:无法得知执行到哪,也无法取消,只能被动的等 resolve 或者 reject 执行或者抛错。
    思路:外部包裹一层 Promise,并对外提供 abort 方法,这个 abort 方法可以用来 reject 内部的 Promise 对象。
    

    题4:给你若干promise对象,如何保证顺序输出?

    // 题:
    const makePromise = (value, time) => {
      return new Promise((resolve, reject) => {
        setTimeout(function() {
          resolve(value);
        }, time)
      })
    };
    function order(promises) {
    }
    order([
      makePromise('a', 3000),
      makePromise('b', 5000),
      makePromise('c', 2000),
    ]);
    // 答案
    function order(promises) {
      let dataArr = []
      const promise = Promise.resolve();
      for (let i = 0; i < promises.length; i++) {
        promise = promise.then((data) => {
          if (data) {
            dataArr.push(data);
          }
          return promises[i];
        });
      }
      return promise.then(data => {
        console.log(data);
      })
    }
    

    题5:如何并行发送指定数量的请求?

    const delay = function delay(interval) {
         return new  Promise((resolve, reject) => {
    		 setTimeout(() => {
    			resolve(interval);
             }, interval);
         })
    }
    let tasks = [
          () => { return delay(1000)},
          () => { return delay(1003)},
          () => { return delay(1005)},
          () => { return delay(1002)},
    ]
    function createRequest(tasks, limit, callback) {
          if(typeof limit === 'function') {
              callback = limit;
    	      limit = 2;
    	  }
          if(typeof limit !== 'number') limit = 2;
          if(typeof callback !== 'function') callback = function() {}
          class TaskQueue {
              constructor() {
                 this.index = 0;
                 this.queue = [];
                 this.results = [];
              }
              pushTask(task) {
                 this.queue.push(task);
                 this.next();
              }
              next() {
                 while(this.index < limit && this.queue.length) {
                    this.index++;
                    const task = this.queue.shift();
                    task().then(result => {
                         this.results.push(result);
                    }).finally(() => {
                        this.index--;
                        this.next();
                    })
                 }
                 if(this.index === 0) callback(this.results);
             }
         }
         const taskQueue = new TaskQueue;
         tasks.map(task => taskQueue.pushTask(task));
    }
    createRequest(tasks, 2, (results) => {console.log(results)})
    

    题6

    const promise1 = new Promise((resolve, reject) => { reject() }) 
    promise1.then(null, function() {return 123})
            .then(null, null)
            .then(
                (value) => { console.log('已经完成', value) },
                (reason) => { console.log('已经失败', reason) }
            )
    // 已经完成 ,123  then中onFulfilled,onRejected不是一个函数,可以被忽略,promise1.then返回一个值123,进入onFulFilled状态。
    

    结语

    关于promise的介绍就到这啦,需要小伙伴们耐心看完。如有不对的地方,肯请指出哦?!


    起源地下载网 » 从入门到手写Promise

    常见问题FAQ

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

    发表评论

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

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

    联系作者

    请选择支付方式

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