Promise

92 阅读4分钟

JavaScript 异步编程

Promise 解决异步编程的问题,其3个状态:等待pengding 成功fulfilled 失败rejected不可逆 image.png

  1. 基本用法
const promise = new Promise(function (resolve, reject) {
  // 这里用于“兑现”承诺

  // resolve(100) // 承诺达成

  reject(new Error('promise rejected')) // 承诺失败
})

promise.then(function (value) {
  // 即便没有异步操作,then 方法中传入的回调仍然会被放入队列,等待下一轮执行
  console.log('resolved', value)
}, function (error) {
  console.log('rejected', error)
})

console.log('end') //因为是同步任务,这里end会被先打印 
  1. 使用案例
function ajax (url) {
  return new Promise(function (resolve, reject) {
    var xhr = new XMLHttpRequest()
    xhr.open('GET', url)
    xhr.responseType = 'json'
    xhr.onload = function () {
      if (this.status === 200) {
        resolve(this.response)
      } else {
        reject(new Error(this.statusText))
      }
    }
    xhr.send()
  })
}

ajax('/api/foo.json').then(function (res) {
  console.log(res)
}, function (error) {
  console.log(error)
})
  1. 常见误区
function ajax (url) {
  return new Promise(function (resolve, reject) {
    var xhr = new XMLHttpRequest()
    xhr.open('GET', url)
    xhr.responseType = 'json'
    xhr.onload = function () {
      if (this.status === 200) {
        resolve(this.response)
      } else {
        reject(new Error(this.statusText))
      }
    }
    xhr.send()
  })
}

// 嵌套使用 Promise 是最常见的误区
// ajax('/api/urls.json').then(function (urls) {
//   ajax(urls.users).then(function (users) {
//     ajax(urls.users).then(function (users) {
//       ajax(urls.users).then(function (users) {
//         ajax(urls.users).then(function (users) {

//         })
//       })
//     })
//   })
// })
  1. 链式调用--避免嵌套使用
  • promise对象的then方法会返回一个全新的promise对象
  • 后面的then方法就是在为上一个then返回的promise注册回调
  • 前面then方法中回调函数的返回值会作为后面then方法回调的参数
  • 如果回调中返回的是promise,那后面then方法的回调会等待它的结果
function ajax (url) {
  return new Promise(function (resolve, reject) {
    var xhr = new XMLHttpRequest()
    xhr.open('GET', url)
    xhr.responseType = 'json'
    xhr.onload = function () {
      if (this.status === 200) {
        resolve(this.response)
      } else {
        reject(new Error(this.statusText))
      }
    }
    xhr.send()
  })
}

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

// var promise2 = promise.then(
//   function onFulfilled (value) {
//     console.log('onFulfilled', value)
//   },
//   function onRejected (error) {
//     console.log('onRejected', error)
//   }
// )

// console.log(promise2 === promise) 
//以上打印的是false,因为then返回的对象,并不是原先的promise对象,而是一个新生成的promise对象

ajax('/api/users.json')
  .then(function (value) {
    console.log(1111)
    return ajax('/api/urls.json')
  }) // => Promise
  .then(function (value) {//如果上一个then返回的是一个promise对象,则接下来的then接收的就是该对象;
    console.log(2222)
    console.log(value) //该处打印的值是上一个then返回的promise对象的值
    return ajax('/api/urls.json')
  }) // => Promise
  .then(function (value) {
    console.log(3333)
    return ajax('/api/urls.json')
  }) // => Promise
  .then(function (value) {
    console.log(4444)
    return 'foo'
  }) // => Promise
  .then(function (value) {//如果上一个then返回的不是promise对象,则返回的值会作为后面then方法回调的参数
    console.log(5555)
    console.log(value) //该处打印的值是foo
  })
  1. 异常处理
  • then方法的第一个参数是成功的回调,第二个参数是失败的回调
  • 使用 catch 注册失败回调是更常见,也更适合链式调用
function ajax (url) {
  return new Promise(function (resolve, reject) {
    // foo()
    // throw new Error()
    var xhr = new XMLHttpRequest()
    xhr.open('GET', url)
    xhr.responseType = 'json'
    xhr.onload = function () {
      if (this.status === 200) {
        resolve(this.response)
      } else {
        reject(new Error(this.statusText))
      }
    }
    xhr.send()
  })
}

ajax('/api/users11.json')
.then(function onFulfilled (value) {
    console.log('onFulfilled', value)
}, function onRejected (error) {
    console.log('onRejected', error)
})

//使用 catch 注册失败回调
ajax('/api/users11.json')
  .then(function onFulfilled (value) {
    console.log('onFulfilled', value)
  })
  .catch(function onRejected (error) {
    console.log('onRejected', error)
  })

// then(onRejected) 实际上就相当于 then(undefined, onRejected)
ajax('/api/users11.json')
  .then(function onFulfilled (value) {
    console.log('onFulfilled', value)
  })
  .then(undefined, function onRejected (error) {
    console.log('onRejected', error)
  })
  
// 同时注册的 onRejected 只是给当前 Promise 对象注册的失败回调
// 它只能捕获到当前 Promise 对象的异常
ajax('/api/users.json')
  .then(function onFulfilled (value) {
    console.log('onFulfilled', value)
    return ajax('/error-url')
  }, function onRejected (error) {
    console.log('onRejected', error)
  })
  
// 因为 Promise 链条上的任何一个异常都会被一直向后传递,直至被捕获
// 分开注册的 onRejected 相当于给整个 Promise 链条注册失败回调
ajax('/api/users.json')
  .then(function onFulfilled (value) {
    console.log('onFulfilled', value)
    return ajax('/error-url')
  }) // => Promise {}
  .catch(function onRejected (error) {
    console.log('onRejected', error)
  })
  
// 全局捕获 Promise 异常,类似于 window.onerror
window.addEventListener('unhandledrejection', event => {
  const { reason, promise } = event

  console.log(reason, promise)
  // reason => Promise 失败原因,一般是一个错误对象
  // promise => 出现异常的 Promise 对象

  event.preventDefault()
}, false)

// Node.js 中使用以下方式
process.on('unhandledRejection', (reason, promise) => {
  console.log(reason, promise)
  // reason => Promise 失败原因,一般是一个错误对象
  // promise => 出现异常的 Promise 对象
})
  1. 静态方法
  • Promise.resolve()
  • Promise.reject()
function ajax (url) {
  return new Promise(function (resolve, reject) {
    // foo()
    // throw new Error()
    var xhr = new XMLHttpRequest()
    xhr.open('GET', url)
    xhr.responseType = 'json'
    xhr.onload = function () {
      if (this.status === 200) {
        resolve(this.response)
      } else {
        reject(new Error(this.statusText))
      }
    }
    xhr.send()
  })
}

Promise.resolve('foo')
  .then(function (value) {
    console.log(value)  // 打印的是foo
  })
  
new Promise(function (resolve, reject) {
  resolve('foo')
})
//以上两个是等价的

var promise = ajax('/api/users.json')
var promise2 = Promise.resolve(promise)
console.log(promise === promise2)   //打印的是true,说明resolve返回的对象,就是promise创建的对象

// 如果传入的是一个 Promise 对象,Promise.resolve 方法原样返回
// Promise.resolve 会将这个对象作为 Promise 执行
Promise.resolve({
  then: function (onFulfilled, onRejected) {
    onFulfilled('foo')
  }
})
.then(function (value) {
  console.log(value)
})

// Promise.reject 传入任何值,都会作为这个 Promise 失败的理由
Promise.reject(new Error('rejected'))
  .catch(function (error) {
    console.log(error)
  })
  
Promise.reject('anything')
  .catch(function (error) {
    console.log(error) //打印的是anything
  })
  1. 并行执行与 Promise.all() 必须全部执行完毕后,才会触发then,返回的是一个数组
function ajax (url) {
  return new Promise(function (resolve, reject) {
    // foo()
    // throw new Error()
    var xhr = new XMLHttpRequest()
    xhr.open('GET', url)
    xhr.responseType = 'json'
    xhr.onload = function () {
      if (this.status === 200) {
        resolve(this.response)
      } else {
        reject(new Error(this.statusText))
      }
    }
    xhr.send()
  })
}

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

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

promise.then(function (values) {
  console.log(values) //这里返回的values是数组
}).catch(function (error) {
  console.log(error)
})

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