说明

在Promise类型当中还有几个静态方法也经常会用到,那我们依次来看一下。

那这个方法的作用就是快速的把一个值转换为一个Promise对象,例如我们通过Promise.resolve传入一个foo的字符串, 那他就直接回返回一个状态为Fulfilled的Promise对象,也就是成功的Promise对象。

Promise.resolve('foo').then(function (value) {
    console.log(value) // foo
})

那这里的foo字符串就会作为这个Promise对象所返回的值,也就是说我们在他的onFulfilled回调当中拿到的参数就是foo这样一个字符串。

那这种方式他完全等价于我们通过new Promise对象的这种方式然后我们在执行函数当中直接去resolve这个foo字符串。

new Promise(function (resolve, reject) {
    resolve('foo')
})

另外,这个方法如果接收到的是另一个Promise对象,那这个Promise对象会被原样返回。

例如我们这里先通过ajax去创建一个promise对象,然后我们再把这个对象传入到Promise.resolve方法当中得到第二个promise对象,我们可以对比发现他们是相等的。

var promise = ajax('/api/users.json')
var promise2 = Promise.resolve(promise)

console.log(promise === promise2) // true

也就是说我们通过Promise.resolve去包装一个promise对象实际上得到的就是原本的这样一个promise。

那还有一个特殊情况就是,如果我们传入的是一个对象而且这个对象也有一个跟promise一样的then方法,也就是说在这个方法中可以接受到onFulfilled和onRejected两个回调,那我们去调用onFulfilled去传入一个值。

Promise.resolve({
    then: function (onFulfilled, onRejected) {
        onFulfilled('foo')
    }
}).then(function (value) {
    console.log(value) // foo
})

那这样一个对象也可以作为一个promise对象被执行,我们在后面的then方法当中,他也能拿到这里所对应传入的值。

那带有这种then方法的对象,可以说是实现了一个叫做thenable的接口,也就是说他是一个可以被then的对象。

那支持这种对象的原因是因为在原生Promise对象还没有普及之前很多时候我们都是使用第三方的库去实现的Promise,那如果说我们现在需要把一些第三方的Promise对象去转换成原生的Promise那就可以借助这样一个机制,因为在第三方的Promise对象当中他也有相同的这种then方法。那我们就可以通过Promise.resolve把他转成一个原生的Promise对象。那这一点仅作为了解就可以了。

那除了Promise.resolve方法还有一个与之对应的叫做Promise.reject方法,那他的作用呢就是快速创建一个一定是失败的Promise对象。

Promise.reject(new Error('rejected')).catch(function (error) {
    console.log(error)
})

那这个方法的参数相对来说就没有那么多情况了,因为我们无论传入什么样的数据,那这个传入的参数都会作为这个promise失败的理由也就是失败的原因。

并行执行

前面介绍的操作都是通过Promise去串联执行多个异步任务,也就是一个任务结束过后再去开启下一个任务。

那相比于传统回调的方式Promise他提供了更扁平的异步编程体验,那如果我们需要同时并行执行多个异步任务,Promise呢也可以提供更为完善的体验。

例如我们在页面中经常涉及到要请求多个接口的情况,那如果说这些请求相互之间没有什么依赖,那我们最好的选择呢就是同时去请求他们,这样避免我们一个一个依次去请求会消耗更多的时间。

那这种并行请求其实很容易实现,我们只需要单独去调用这里的ajax函数就可以了。

ajax('/api/users.json')
ajax('/api/users.json')

但是麻烦的是我们怎么样去判断所有的请求都已经结束了那样一个时机,那传统我们的做法是定义一个计数器,然后每结束一个请求我们让这个计数器累加一下,直到我们这个计数器的数量跟我们任务的数量相等时就表示所有的任务都结束了。

那这种方法呢会非常的麻烦,而且呢我们需要去考虑出现异常的情况,那在这种情况下我们使用Promise类型的all方法就会简单的多,因为这个方法他可以将多个Promise合并成一个Promise统一去管理。

那我们具体来看如何去使用。

Promise.all方法他需要接收的是一个数组,然后数组中的每一个元素都是一个Promise对象,我们可以把这些Promise都看作是一个一个的异步任务。

var promise = Promise.all([
    ajax('/api/users.json'),
    ajax('/api/users.json')
])

那这个方法会返回一个全新的Promise对象, 当内部所有的Promise都完成过后呢,我们所返回的这个全新的Promise才会完成,那此时我们这个Promise对象他拿到的结果就是一个数组,在这个数组中包含着每一个异步任务执行过后的结果。

promise.then(function (values) {
    console.log(values)
}).catch(function (error) {
    console.log(error)
})

那需要注意的是,在这个任务的过程当中,只有当所有的任务都成功结束了我们这里新的Promise他才会成功结束,如果说其中有任何一个任务失败了,那这个Promise就会以失败结束。

那这是一种很好的同步执行多个Promise的方式,那这里我们可以再综合使用一下串联。

我们先去通过ajax去请求一下数据,那这个地址请求完是包含所有地址的对象。然后我们这里通过Object.values方法获取到这个对象所有的属性值,也就是所有的url地址组成的一个数组,那有了这样一个数组过后我们就可以使用数组对象的map方法,将这个字符串数组去转换成一个包含请求任务的promise数组。

那完成以后我们就可以使用Promise.all将这个promise数组组合成一个新的promise 然后return掉。那这样的话,我们就可以在下一个then方法中,我们就可以拿到当前我们这里我们这个Promise数组当中每一个异步请求得到的结果数据。

ajax('/api/urls.json').then(value => {
    const urls = Object.values(value)
    const tasks = urls.map(url => ajax(url))
    Promise.all(tasks)
}).then( values => {
    console.log(values);
})

那这就是我们组合使用串行和并行这两种方式,那这里的执行过程呢肯定是先去请求的所有url地址,然后把我们的urls地址拿到过后会去同时请求urls数组当中所有的地址。

另外呢Promise除了提供了一个叫做all的方法以外,他还提供了一个叫做race的方法,那这个方法呢,他同样可以把多个promise对象组装成一个新的promise对象。

但是呢与Promise.all有所不同的是,Promise.all是等待所有的任务结束后才会结束,而Promise.race他是跟着我们所有任务当中第一个完成的任务一起结束。

也就是说只要有任何一个任务完成了那我们所返回的新的promise对象也会完成。

例如我们这里先去调用ajax函数去发送一个请求,那这样就可以得到一个promise对象,然后我们再去单独的创建一个独立的promise对象,那这个promise对象的内部呢我们使用setTimeout在500毫秒之后去以失败的方式reject。

const request = ajax('/api/posts.json')
const timeout = new Promise(function (resolve, reject) {
    setTimeout(function () {
        reject(new Error('timeout'))
    }, 500)
})

Promise.race([
    request,
    timeout
]).then(value => {
    console.log(value)
}).catch(error => {
    console.log(error)
})

那此时呢如果我们使用Promise.race将这两个promise对象合并到一起,那这样的效果就是如果说我们500毫秒之内这个请求完成了,那我们就可以正常得到响应结果,那如果说500毫秒过后我们这个请求他就没有办法把结果返回回来了。

因为在500毫秒过后呢,我们第二个promise会以失败的方式结束。

而race方法就是以第一个结束的promise为准。我们可以在谷歌浏览器的网路限速中尝试,我们可以选择一个相对较慢的网速。

我们可以看到一个超时的异常出现,这也就是我们经常用来去实现ajax请求超时控制的一种方式。

那总结一下我们这里介绍了两个能够把多个promise对象组合到一起的方法,分别是Promise.all和Promise.rece。

那这两个方法最简单的区别就是Promise.all会等待所组合的所有promise都结束而且是成功结束,才会成功完成。而Promise.race他只会等待第一个结束的任务。