Promise
对象是一个构造函数,用来生成Promise
实例,构造函数接受一个函数作为参数,该函数的两个参数分别是resolve
和reject
,它们是两个函数,由JavaScript引擎提供,不用自己部署。
其中,resolve
函数在异步操作成功时调用,并将异步操作的结果作为参数传递出去;
reject
函数在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。
来看一个Promise
的例子:
上面代码中,timeout
方法返回一个Promise
实例,表示一段时间以后才会发生的结果,这里的setTimeout
相当于一个异步操作,过了100ms
以后异步操作成功返回后调用resolve
,此时done
作为resolve
的参数同样会传给then
的第一个函数。
再来一个异步加载图片的例子比较接地气:
这里例子就很直观了,使用Promise
包装了一个图片加载的异步操作,如果加载成功,就调用resolve
方法,否则就调用reject
方法。
下面是用Promise
对象实现Ajax
操作的例子:
上面代码中,getJSON
是对XMLHttpRequest
对象的封装,用于发出一个针对JSON
数据的HTTP
请求,并且返回一个Promise
对象。如果调用resolve
函数和reject
函数时带有参数,那么它们的参数会被传递给回调函数
上面代码中,总结就是以下两点:
Promise
实例新建后会立即执行
resolve
最后才会输出)
resolve
函数的参数除了正常的值以外,还可能是另一个Promise
实例:
即,一个异步操作的结果是返回另一个异步操作。
注意,这时p1
的状态就会传递给p2
,也就是说,p1
的状态决定了p2
的状态。如果p1
的状态是pending
,那么p2
的回调函数就会等待p1
的状态改变;如果p1
的状态已经是resolved
或者rejected
,那么p2
的回调函数将会立刻执行。
前面也说过,Promise
新建后就会立即执行,所以直接就会进入函数体。由于p2
返回的是另一个Promise
,导致p2
自己的状态无效了,由p1
的状态决定p2
的状态。所以,后面的then
语句都变成针对后者(p1
)。又过了
2 秒,p1
变为rejected
,导致触发catch
方法指定的回调函数。
具体什么意思呢,看下面的例子:
上面代码中,调用resolve(1)
以后,后面的console.log(2)
还是会执行,并且会首先打印出来。这是因为立即 resolved
的Promise
是在本轮事件循环的末尾执行,总是晚于本轮循环的同步任务。
的使命就完成了,后继操作应该放到then
方法里面,而不应该直接写在resolve
或reject
的后面。所以,最好在它们前面加上return
语句,这样就不会有意外。
100ms
后调用resolve
函数,resolve
函数内部将状态设为了resolved
了,然后在本脚本其他同步惹任务都完成后执行then
方法返回的回调函数,此时将value
值传给then
方法的回调函数。
如果一个接口访问时间超过400毫秒就算超时,用Promise
如何解决
传统的解决思路设个定时器,设个全局变量data接受接口要返回的数据,如果400毫秒后定时器的全局data还未被赋值,说明接口请求超时,当然这种做法不够优雅,可以使用Promise
来实现:
虽然看起来好像是解决了,但是怎么看怎么像愚蠢的解决方案嵌套了一层美丽的皮囊,下面结合Promise
的race
方法来更好的解决超时需求: