EXIT

EXIT

00:00:00

Promise

[toc]


问题:为什么要用Promise,他有什么优点吗?

答:因为Promise支持链式调用,可以解决回调地狱的问题。

Promise的衍生方法

--Promise.all(promises)

Promises:多个promise的数组,使用该方法可以返回一个promise,只有promises数组中所有的promise都成功才算成功,只要有一个失败,就直接失败。

--Promise.allSettled(promises)

Promises:多个promise的数组,使用该方法可以返回一个promise,promise.then(res=>res)可以返回所有promises数组里的执行结果数组res,不管是成功还是失败。返回的res表示形式为:返回数组顺序为输入顺序

[ { status: 'fulfilled', value: 1 }, { status: 'rejected', reason: 2 }, { status: 'rejected', reason: 3 } ]

--Promise.race(promises)

Promises:多个promise的数组,使用该方法可以返回一个promise,promises数组中第一个完成的promise的结果就为该方法的最终状态,无论成败。

--Promise.any(promises)

Promises:多个promise的数组,使用该方法可以返回一个promise,promises数组中第一个状态变为fulfilled的promise的结果为该方法的最终状态。如果数组中所有promise都为reject,则返回一个失败的promise,失败的原因为所有失败promise原因的集合。

Promise异常穿透

由于promise链式调用的特性,我们可以在所有then链式调用的最后,指定失败的回调,前面操作出现的任何异常,都会传到最后的失败的回调中处理。

如何中断Promise链式调用

在需要中断的地方加上 return new Promise(()=>{})即可中断。原理,返回的是一直pendding状态的promise,不会调用后面的then方法。

手写Promise

初立框架

首先,在使用Promise时,我们都需要new Promise,因此,Promise实质上是一个类,那么我们要首先创建这个类。并且要知道这个类上有哪些方法。

//创建Promise let p = new Promise((resolve,reject)=>{ resolve("ok"); }) p.then((res)=>res,(rej)=>rej)

由上可知:

  • Promise本身具有一个状态属性值和结果属性值。
  • Promise接收一个函数,我们可以称它为exactor,executor函数接收两个参数 - resolve,reject,这两个参数也是函数。resolve,reject方法也接收参数data,即返回的数据。
  • new Promise出来的实例对象有方法.then(),then()方法接收两个参数,这两个参数也是函数,一个是res函数,一个是reject函数。then方法会return一个新的Promise对象好方便进行链式调用。

此时我们可以搭建出来一个框架:

坑:由于resolve是直接调用的,所以this得指向是全局window

解决办法:1 将this在外面进行额外保存 2 resolve reject使用箭头函数

function MyPromise(executor){ //添加promise属性值 this.PromiseState = "pending"; this.PromiseResult = null; //resolve函数 const resolve = (data) =>{ } //reject函数 const reject = (data) =>{ } //执行器函数是同步调用的。 executor(resolve,reject); } //添加then方法 MyPromise.prototype.then = function(onReslove,onReject){ }

完善resolve、reject函数

由于resolve和reject方法可以改变Promise状态和Promise结果值,我们可以对resolve和reject函数进行进一步的完善。

//resolve函数 const resolve = (data) =>{ this.PromiseState = "fulfilled"; this.PromiseResult = data; } //reject函数 const reject = (data) =>{ this.PromiseState = "rejected"; this.PromiseResult = data; }

另外,抛出异常也会改变Promise的状态和结果值。采用try...catch来处理异常。另外,我们都知道抛出异常其实是在executor函数中进行的,因此我们的try...catch应该包裹在executor函数中进行。

//try...catch捕获异常并改变Promise的状态和结果值。 try { //执行器函数是同步调用的。 executor(resolve,reject); } catch (error) { reject(error); }

此时整体框架为:

function MyPromise(executor){ //添加promise属性值 this.PromiseState = "pending"; this.PromiseResult = null; //resolve函数 //resolve功能:1.修改Promise的状态 2.修改promise的结果 const resolve = (data) =>{ this.PromiseState = "fulfilled"; this.PromiseResult = data; } //reject函数 const reject = (data) =>{ this.PromiseState = "rejected"; this.PromiseResult = data; } //try...catch捕获异常并改变Promise的状态和结果值。 try { //执行器函数是同步调用的。 executor(resolve,reject); } catch (error) { reject(error); } } //添加then方法 MyPromise.prototype.then = function(onReslove,onReject){ }

状态只能修改一次

在改变状态之前,应该判断一下状态是否已经被更改了。

//在resolve和reject函数内部添加一条判断语句即可 if (this.PromiseState!=="pending") return;

同步任务then方法的实现

此时我们对then方法进行完善,then方法接收两个函数参数,在成功时和失败时分别调用。

//添加then方法 MyPromise.prototype.then = function(onReslove,onReject){ //成功时回调-同步 if(this.PromiseState==="fulfilled") onReslove(this.PromiseResult); //失败时回调-同步 if(this.PromiseState==="rejected") onReject(this.PromiseResult); }

异步任务then方法的实现

以上我们的实现都是在同步任务时进行实现的,但是若在异步任务时,我们上面的方法就不好使了。

在进行异步任务时,executor里面掺杂了定时器,resolve或者reject函数还未执行,而是丢进了宏任务队列,此时代码继续向下走,走到.then方法时执行,由于此刻的Promise实例状态并未改变,因此then方法并不执行。所以,我们应该给.then方法再加一个判断,即,当状态为pending时,我们需要做些事情...

做什么事情呢?

我们都知道,在异步任务时,我们的then回调函数肯定是不能立即执行的,只有当executor中调用了resolve/reject函数之后才执行,那么就表明,我们的then回调函数应该在resolve/reject函数中执行。

怎么把then回调函数放入resolve/reject函数中执行呢?

在实例对象上新添加一个属性callbacks,此为then方法的回调函数,并在resolve/reject函数中执行。

function MyPromise(executor){ ... this.callbacks = {}; ... const resolve = (data) =>{ ... //异步:调用成功的回调函数 this.callbacks?.onReslove(data) ?? null; } const reject = (data) =>{ ... //异步:调用失败的回调函数 this.callbacks?.onReject(data) ?? null; } ... } //添加then方法 MyPromise.prototype.then = function(onReslove,onReject){ //成功时回调-同步 if(this.PromiseState==="fulfilled") onReslove(this.PromiseResult); //失败时回调-同步 if(this.PromiseState==="rejected") onReject(this.PromiseResult); //异步时执行-异步 if(this.PromiseState==="pending") this.callbacks = {onReslove,onReject}; }

指定多个回调的实现

本小节的意思是:

p.then()....之后 再次p.then()...应该继续执行(两者都执行),所以我们需要将所有的then回调函数都放到callback里面,所以我们的代码应该进行适当的改变,将callback改成数组。

function MyPromise(executor){ ... this.callbacks = []; ... const resolve = (data) =>{ ... //异步:调用成功的回调函数 this.callbacks.forEach((val)=>{ val?.onReslove(data); }) } const reject = (data) =>{ ... //异步:调用失败的回调函数 this.callbacks.forEach((val)=>{ val?.onReject(data); }) } ... } //添加then方法 MyPromise.prototype.then = function(onReslove,onReject){ //成功时回调-同步 if(this.PromiseState==="fulfilled") onReslove(this.PromiseResult); //失败时回调-同步 if(this.PromiseState==="rejected") onReject(this.PromiseResult); //异步时执行-异步 if(this.PromiseState==="pending") this.callbacks.push({onReslove,onReject}); }

同步任务then方法的返回

我们知道,then方法返回的应该也是一个Promise对象,返回的Promise对象的状态和结果会根据.then方法返回的值进行判断:

  • 如果.then方法返回的是undefined或者其他非promise类型的值,则返回的promise对象为fulfilled,结果为.then方法返回值
  • 如果.then方法返回的是一个新的promise对象,则之前返回的promise对象状态和值要根据新的promise对象状态和值进行更新
//添加then方法 MyPromise.prototype.then = function(onReslove,onReject){ //返回一个promise对象 , 注意:new Promise中new关键字就相当于一个函数fn进行了fn(); return new MyPromise((resolve,reject)=>{ //成功时回调-同步 if(this.PromiseState==="fulfilled") { let res = onReslove(this.PromiseResult); //如果返回是Promise对象 if(res instanceof MyPromise){ res.then(v=>resolve(v),r=>reject(r)) } //如果返回不是promise对象 else{ resolve(res); } } //失败时回调-同步 if(this.PromiseState==="rejected"){ let res = onReject(this.PromiseResult); //如果返回是Promise对象 if(res instanceof MyPromise){ res.then(v=>resolve(v),r=>reject(r)) } //如果返回不是promise对象 else{ reject(res); } } //异步时执行-异步 if(this.PromiseState==="pending") this.callbacks.push({onReslove,onReject}); }) }

异步任务then方法的返回

由于在异步任务中,其then回调函数的执行放在了callbacks中,由于我们需要知道then回调函数的执行结果,因此,我们需要对then函数部分的异步状况进行改进:

MyPromise.prototype.then = function(onReslove,onReject){ ... if(this.PromiseState==="pending") { let obj = { onReslove:()=>{ let res = onReslove(this.PromiseResult); if(res instanceof MyPromise){ res.then(v=>resolve(v),r=>reject(j)); } else{ resolve(res); } }, onReject:()=>{ let res = onReject(this.PromiseResult); if(res instanceof MyPromise){ res.then(v=>resolve(v),r=>reject(j)); } else{ resolve(res); } } } this.callbacks.push(obj) ... }

异常穿透方法的实现

MyPromise.prototype.then = function(onReslove,onReject){ //如果没有传onReslove和onReject,给一个默认初始值 if(typeof onReslove!=="function"){ onReslove = value => value; } if(typeof onReject!=="function"){ onReslove = reason => {throw reason}; } ... } //添加异常穿透方法 MyPromise.prototype.catch = function(onReject){ return this.then(undefined,onReject); }
uid:lruMcF
VOIDIS.ME
  1. no-like
  2. message
  3. Bilibili
  4. Github
  5. RSS
  6. sun