promise - lucky0604/web-development GitHub Wiki

Promise

Promise三种状态

  • Pending unresolved,即不是resolve也不是reject状态,属于Promise实例刚创建的初始化状态。
  • Fulfilled has-resolution,成功(resolve)时,此时会调用onFulfilled
  • Rejected has-rejection,失败(reject)时,此时会调用onRejected

Tips: Promise对象的状态,从Pending转换为FulfilledRejected之后,这个对象的状态就不会再发生任何变化,即,在.then()后执行的函数只会被调用一次。 FulfilledRejected这两个中的任一状态都可以表示为不变的Settled

创建Promise实例的快捷方式

Promise.resolve

可以认为静态方法Promise.resolve(value)new Promise()的快捷方式,比如Promise.resolve(1)可以认为是下面代码的语法糖:

new Promise(function(resolve) {
  resolve(1)
})

其中resolve(1)会让promise实例对象直接进入resolve状态,并将1传递给后面将要调用的.then()方法里所指定的onFulfilled函数。

Tips: Promise.resolve(value)返回值是一个promise对象,因此可以直接对其进行.then()调用

另一个作用

Promise.resolve方法另外一个作用是将thenable对象转换成promise对象

thenable: 带有then的方法

可以认为Promise.resolve的作用就是将传递给它的参数填充(Fulfilled)promise对象后并返回这个promise对象

Promise.reject

Promise.reject(error)Promise.resolve(value)是类似的静态方法,也是创建Promise实例的快捷方式 即Promise.reject(error)方法等同于

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

Promise chain

promise对象的方法链长度变得很长时:

function taskA() {
  console.log('Task A')
}
function taskB() {
  console.log('Task B')
}
function onRejected(error) {
  console.log('Catch error: A or B', error)
}
function finalTask() {
  console.log('Final Task')
}

var promise = Promise.resolve()
promise.then(taskA)
  .then(taskB)
  .catch(onRejected)
  .then(finalTask)

上面代码没有为then方法指定第二个参数,所以可以理解为:

  • then 注册onFulfilled时的回调函数
  • catch 注册onRejected时的回调函数

taskAtaskB的处理中,下面的情况会调用onRejected方法

  • 发生异常的时候
  • 返回了一个Rejected状态的promise对象

使用reject而不是throw

当发生异常时,会被catch捕获并被由在此函数注册的回调函数进行错误处理。 另外一种异常处理策略是通过返回一个Rejected状态的promise对象来实现,这种方法不通过使用throw new Error()就能在promise chain中对onRejected进行调用。

function taskA() {
  console.log('Task A')
  throw new Error('throw Error at Task A')
}
function taskB() {
  console.log('Task B')
}
function onRejected(error) {
  console.log(error)
}
function finalTask() {
  console.log('Final Task')
}

var promise = Promise.resolve()
promise.then(taskA)
  .then(taskB)
  .catch(onRejected)
  .then(finalTask)

这里在taskA中使用throw方法故意制造了一个异常,但在实际中想主动进行onRejected调用时,应该返回一个Rejected状态的promise对象。

Promise的构造函数,以及被then调用执行的函数基本上都可以认为是在try...catch代码块中执行的,所以在这些代码中即使使用throw,程序本身也不会因为异常而终止。 如果在Promise中使用throw语句的话,会被try...catch住,最终promise对象也变为Rejected状态

var promise = new Promise(function(resolve, reject) {
  throw new Error('message')
})
promise.catch(function(error) {
 console.error(error)       // output: 'message'
})

代码运行时不会出现问题,但是如果想把promise对象状态设置为Rejected状态,使用reject方法则更显得合理

var promise = new Promise(function(resolve, reject) {
  reject(new Error('message'))
})
promise.catch(function(error) {
  console.error(error)    // output: 'message'
})

promise chain中传递参数

上面代码中,task都是相互独立,只是被简单调用,此时若需要taskAtaskB传递一个参数,即taskAreturn的返回值,会在taskB执行时传递给它

function doubleUp(value) {
  return value * 2
}
function increment(value) {
  return value + 1
}
function output(value) {
  console.log(value)
}
var promise = Promise.resolve(1)
promise.then(increment)
  .then(doubleUp)
  .then(output)
  .catch(function(error) {
    // promise chain中出现异常的时候会被调用
    console.error(error)
  })

整体执行流程为:

  • Promise.resolve(1),传递1给increment函数
  • 函数increment对接收的参数进行+1操作并返回(通过return)
  • 此时参数变为2,并再次传给doubleUp函数
  • 最后在函数output中打印结果

每个方法中return的值不仅只局限于字符串或者数值类型,也可以是对象或者promise对象等复杂类型。 return的值会由Promise.resolve(return的返回值),进行相应的包装处理,因此不管回调函数中会返回一个什么样的值,最终then的结果都是返回一个新创建的promise对象。

也就是说,.then()不仅仅是注册一个回调函数这么简单,还会将回调函数的返回值进行变换,创建并返回一个promise对象

使用Promise.then()处理多个异步请求

处理多个异步请求,Promise.all()比较适合,这里故意采用大量.then()的写法

function getUrl(URL) {
  return new Promise(function(resolve, reject) {
    var req = new XMLHttpRequest()
    req.open('GET', URL, true)
    req.onload = function() {
      if (req.status === 200) {
        resolve(req.responseText)
      } else {
        reject(new Error(req.statusText))
      }
    }
    req.onerror = function() {
      reject(new Error(req.statusText))
    }
    req.send()
  })
}
var request = {
  comment: function getComment() {
    return getUrl('http://localhost').then(JSON.parse)
  },
  people: function getPeople() {
    return getUrl('http://localhost').then(JSON.parse)
  }
}
function main() {
  function recordValue (results, value) {
    results.push(value)
    return results
  }
  // []用来保存初始化的值
  var pushValue = recordValue.bind(null, [])
  return request.comment().then(pushValue).then(request.people).then(pushValue)
}

main().then(function(value) {
  console.log(value)
}).catch(function(error) {
  console.error(error)
})

Promise.all()

Promise.all()接收一个promise对象的数组作为参数,当这个数组里所有promise对象全部变为resolvereject状态时,才会去调用.then()方法 因此可以将上面代码改为:

function main() {
  return Promise.all([request.comment(), request.people()])
}

main().then(function (value) {
  console.log(value)
}).catch(function(error) {
  console.log(error)
})

Promise.all()promise数组并不是一个个的顺序执行,而是同时开始,并行执行。