Promise

作者: 珍珠林 | 来源:发表于2017-05-07 10:53 被阅读0次

在实际的JavaScript开发中,我们需要使用到很多的异步开发。如我们需要在页面中使用JavaScript实现一系列动画效果,那么每个动画效果都要靠异步完成。当每个动画效果完成后,就会通过一个回调函数通知逻辑代码。为了实现动画效果的顺序展示,我们需要把这些动画效果的方法“串”起来:

animate1(() => {
    animate2(() => {
        animate3(() => {
            animate4(() => {
                // ...
            })
        })
    })
})

后来有人提出了Promise概念,这个概念意在让异步代码变得干净和直观:

animate1()
    .then(() => animate2())
    .then(() => animate3())
    .then(() => animate4())
    .then(() => {
        // ...
    })

Promise最大的目的在于可以让异步代码变得井然有序,就如我们需要在浏览器访问一个以JSON作为返回格式的第三方API,在数据下载完成后进行JSON解码,通过Promise来包装异步流程可以使代码变得非常干净整洁:

fetch('http://example.com/api/users/top')
    .then(res => res.json())
    .then(data => {
        //...
    })
    // 处理错误
    .catch(err => console.error(err))

Promise在设计上具有原子性,即只有三种状态:等待(Pending)、成功(Fulfilled)、失败(Rejected)。

基本语法

要想给一个函数赋予Promise的能力,就要先创建一个Promise对象,并将其作为函数值返回。Promise构造函数要求传入一个带有resolve和reject参数的函数。resolve和reject是两个用于结束Promise等待的函数,对应的状态分别为成功和失败:

function asyncMethod(...args) {
    return new Promise((resolve, reject) => {
        // ...
    })
}

将新创建的Promise对象作为异步方法的返回值,所有的状态就可以使用它所提供的放阿飞进行控制了。

进行异步操作

通过resolve(value)和reject(reason)方法来控制Promise的原子状态:

new Promise((resolve, reject) => {
    api.call('fetch-data', (err, data) => {
        if(err) return reject(err)  // 进入成功状态
        resolve(data)  // 进入失败状态
    })
})

在Pomise的首层函数作用域中,一旦出现throw语句,会直接进入失败状态:

(new Promise(function() {
    throw new Error('test')
}))
.catch(err => console.error(err))

但是相对应的return语句并不会使Promise对象进入成功状态。

处理Promise的状态

Promise对象,有两个与resolve(value)和reject(reason)方法对应的用于处理状态变化的方法

方法 方法内容
promise.then(onFulfilled[,onRejected]) 处理成功状态并接收返回值,也可以处理失败状态并处理失败的原因(可选)
promise.catch(onReject) 处理Promise对象的失败状态并处理失败的原因
  1. 如果onFulfilled或onRejected中所返回的值是一个Promise对象;
  2. 如果onFulfilled或onRejected中返回值并不是一个Promise对象,则会返回一个已进入成功状态的Promise对象;
  3. 如果onFulfilled或onRejected中因throw语句而抛出一个错误err,则会返回一个已进入
    失败状态的Promise对象;
    以上返回的Promise对象都会被加入到Promise的处理链中,呈现出流水线作业模式:
asyncMethod()
    .then((...args) => args /* ... */ )
    .catch(err => console.error(err))

因此我们可以利用Promise对象链的流水线作业模式来实现动画效果:

animate1()
    .then(() => animate2())
    .then(() => animate3())
    .then(() => animate4())
    .then(() => {
        // ...
    })

Promise对象链的传递性

以往回调的方式,一旦某个环节出了错误,就需要在每个回调函数中进行错误处理,但在Promise对象链中可以实现集中处理甚至分块处理。Promise对象链中某一环节出现错误,便会从出错的环节开始,不断向下传递,直到出现任何一环的Promise对象对错误进行响应为止:

animate1()
    .then(() => animate2())   // 向下传递
    .then(() => animate3())   // 向下传递
    .then(() => animate4())   // 向下传递
    .catch(err => {
        console.error(err)
        return animate5()
    })
    .then(/* ... */)          // 向下传递
    .catch(err => console.error(err))

Promise工具方法

  • Promise.all(iterable)
    该方法传入一个可迭代对象(如数组),返回一个Promise对象,该Promise对象会在当前可迭代对象的所有Promise对象都进入完成状态(包括成功和失败)后被激活:
const promise = [ async(1), async(2), async(3), async(4) ]

Promise.all(promise)
    .then(values => {
        // ...
    })
    .catch(err => console.error(err))
  • Promise.race(iterable)
    与Promise.all(iterable)类似,不过该Promise对象会在当前可迭代对象的某一Promise对象进入完成状态(包括成功和失败)后就被激活:
const promise = [ async(1), async(2), async(3), async(4) ]

Promise.race(promise)
    .then(values => {
        // ...
    })
    .catch(err => console.error(err))

相关文章

网友评论

      本文标题:Promise

      本文链接:https://www.haomeiwen.com/subject/uglytxtx.html