Promise本质

Promise所说的异步执行,只是将Promise构造函数中resolvereject方法和注册的callback转化为eventLoopmicrotask/Promise Job,并放到EventLoop队列中等待执行,也就是JavaScript单线程中的“异步执行”

构造函数中的输出执行是同步的,输出1,执行resolve函数,将Promise对象状态置为resolved,输出3。同时注册这个Promise对象的回调then函数,将在当前脚本所有同步任务执行完,输出4。整个脚本执行完,stack清空。

eventloop检查到stack为空,再检查microtask队列中是否有任务,发现了Promise对象的then回调函数产生的microtask,推入stack,执行。输出2,event loop的队列为空,stack为空,脚本执行完毕。

Promise的异步处理

Promise的两个固有行为:

  1. 每次对Promise调用 then(..),它都会创建并返回一个新的Promise,我们可以将其链接起来;
  2. 不管从then(..)调用的完成回调(第一个参数)返回的值是什么,它都会被自动设置为被链接Promise(第一点中的)的完成。

使Promise序列真正能够在每一步有异步能力的关键是:Promise.resolve(..)会直接返回接收到的真正Promise,或展开接收到的thenable值,并在持续展开thenable的同时递归地前进。

尝试去理解一下下面这段代码

Promise的错误处理

一个错误/异常是基于每个Promise的,意味着在链条的任意一点捕获这些错误是可能的,而且这些捕获操作在那一点上将链条“重置”,使它回到正常的操作上来

再看一段代码

第 2 步出错后,第 3 步的拒绝处理函数会捕捉到这个错误。拒绝处理函数的返回值(这段 代码中是 3),如果有的话,会用来完成交给下一个步骤(第 4 步)的 promise,这样,这 个链现在就回到了完成状态。

注意这句话,解释了为什么最后会出现4,这里要好好理解透彻

总结起来,Promise的步骤

  • 调用Promise的then(..)会自动创建一个新的Promise从调用返回。
  • 在完成或拒绝处理函数内部,如果返回一个值或抛出一个异常,新返回的可链接的Promise就相应地决议。
  • 如果完成或拒绝处理函数返回一个Promise,它将会被展开,这样一来,不管它的决议值是什么,都会成为当前then(..)返回的链接Promise的决议值。

Promise的穿透

下面这段代码先自己想一下,再去控制台打印

then()受非函数的参数时,会解释为then(null),这就导致前一个Promise的结果穿透到下面一个Promise。

Promise局限性

  1. 顺序错误处理
    Promise链中的错误很容易被无意中默默忽略掉
  2. 单一值
    Promise只能有一个完成值或一个拒绝理由
  3. Promise性能
    Promise进行的动作要多一些,这自然意味着它也会稍慢一些
    更多的工作,更多的保护。这些意味着Promise与不可信任的裸回调相比会更慢一些
    Promise使所有一切都成为异步的了,即有一些立即(同步)完成的步骤仍然会延迟到任务的下一步。这意味着一个Promise任务序列可能 比完全通过回调连接的同样的任务序列运行得稍慢一点

编写Promise代码

创建promise对象

流程如下:

  1. new Promise(fn) 返回一个promise对象
  2. fn 中指定异步等处理
    • 处理结果正常的话,调用resolve(处理结果值)
    • 处理结果错误的话,调用reject(Error对象)

创建一个用Promise把XHR处理包装起来的名为getURL的函数

编写promise对象处理方法

promise对象添加处理方法主要有以下两种:

  • promise对象被resolve时的处理(onFulfilled)
  • promise对象被reject时的处理(onRejected)

getURL只有在通过XHR取得结果状态为200时才会调用resolve- 也就是只有数据取得成功时,而其他情况(取得失败)时则会调用reject方法
getURL函数中的resolve(req.responseText);会将promise对象变为resolve(Fulfilled)状态, 同时使用其值调用onFulfilled函数。
getURL的处理中发生任何异常,或者被明确reject的情况下, 该异常原因(Error对象)会作为.catch方法的参数被调用。

Promise API

Promise.resolve

  • Promise.resolve(promise)
    接收到promise对象参数的时候,返回的还是接收到的promise对象
  • Promise.resolve(thenable)
    接收到thenable类型的对象的时候,返回一个新的promise对象,这个对象具有一个then方法

  • Promise.resolve(object)
    接收其他类型的时候(包括JavaScript对或null等),返回一个将该对象作为值的新promise对象

Promise.reject

  • Promise.reject(object)
    返回一个使用接收到的值进行了reject的新的promise对象。

Promise.then

  • promise.then(onFulfilled, onRejected)
    promise对象会在变为resolve或者reject的时候分别调用相应注册的回调函数。
    • handler返回一个正常值的时候,这个值会传递给promise对象的onFulfilled方法。
    • 定义的handler中产生异常的时候,这个值则会传递给promise对象的onRejected方法

Promise.catch

IE8以下Promise#catch标识符冲突问题:

使用Promise#then代替Promise#catch

Promise.all

  • Promise.all(promiseArray)
    参数传递promise数组中所有的promise对象都变为resolve的时候,该方法才会返回, 新创建的promise则会使用这些promise的值。
    如果参数中的任何一个promisereject的话,则整个Promise.all调用会立即终止,并返回一个reject的新的promise对象。
    由于参数数组中的每个元素都是由Promise.resolve包装(wrap)的,所以Paomise.all可以处理不同类型的promose对象。

Promise.race

  • Promise.race(promiseArray)
    参数promise数组中的任何一个promise对象如果变为resolve或者reject的话, 该函数就会返回,并使用这个promise对象的值进行resolve或者reject

几个不错的例子

  1. 理解三种状态
  1. 链式调用以及返回值

上面说的then()接收两个函数作为参数,返回值有三种情况,可以返回上面看看

  1. 异常处理

Promise中的异常由then参数中第二个回调函数(Promise执行失败的回调)处理,异常信息将作为Promise的值。异常一旦得到处理,then返回的后续Promise对象将恢复正常,并会被Promise执行成功的回调函数处理。

需要注意p1p2 多级then的回调函数是交替执行的 ,这正是由Promise then回调的异步性决定的。

  1. resolvereject的区别

这段毫无疑问,resolve直通车

这段可能会有点疑问,主要在于理解这句代码resolve(Promise.reject('reject'));
再回想一下上面的错误处理以及thenable对象的展开功能,是不是就好理解一点了,其实可以理解为与运算(&&),有一个reject,传下去的也会是reject

但是!!!并不是一直链式的传下去的全都是reject,只是紧跟着的下一个then会收到reject而已,万望好好理解这句话(我这里不展开讲了)

有了第二段的基础,这一段应该就非常好理解了

原文:大专栏  JavaScript知识梳理--Promise


01-19 19:32
查看更多