promise 一种更优的异步编程统一方案

304 阅读3分钟

前言

异步编程采用回调函数的方式,一不小心就会陷入回调地狱

$.get('api1',function(data1) {
  $.get('api2',function(data2) {
    $.get('api3',function(data3) {
  
    })
  })
})

为了避免回调地狱,Common JS社区提出了Promise规范
Promise在ES2015中,被标准化,成为语言规范

定义

定义:Promise 对象用于表示一个异步操作的最终完成 (或失败)及其结果值

graph LR
Promise -->Pending
Pending -->Fulfilled
Pending -->Rejected
Fulfilled -->onFulfilled
Rejected --> onRejected

通过一个故事了解 主线程,Promise,Event loop

故事背景:我是一名连锁餐馆唯一的厨子(主线程)
我对Promise(众多服务员其中之一)说:帮我买一瓶500ML的*牌子的陈醋。
Promise说:好的,这个任务我接下来了。
然后我就切了一个菜就去休眠了(因为其他活都干完了,并且我要分配任务不能离开)。
Promise去买醋了。
Event Loop在作为我的监工,就在等待着Promise回来,Promise回来后,就把我叫醒。
情况A:
Promise对我说:我买的你要的东西了,给你。
我拿到醋就开始烹饪了。
情况B:
Promise对我说,因为*
牌子没有货了,我没有买到你想要的东西。
然后去做补救措施。

上面的故事中
我(主线程)在老板(运行环境)允许的范围内,分配人力。
Promise是一个很有契约精神的人,他承诺了帮我买东西,按照我的指令严格执行,并且最后会告诉我最终结果。
Event Loop就是老板派下来的监工(店长),看我闲下来,并且有活的时候就会叫我来干活。 当然我忙的时候店长也不会来打扰我的工作进度。即使Promise完成了我分配的任务,店长也会让他先等着。

Promise封装ajax

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()
  })
}
const promise = ajax('/api/users.json')
promise.then((res)=> { console.log(res) }, err => { console.log(err) })

promise的本质就是使用回调函数的方式,去定义异步任务结束后需要执行的任务,只不过异步函数是通过then方法传递进去的。

Promise常见误区及链式调用

如果请求B必须依赖请求A返回的结果,很多人又会写成回调地狱,其实promise是可以链式调用的,让我们的异步任务代码扁平化。

ajax('/api1')
  .then(function (value) {
    console.log(1)
    return ajax('/api/value')
  }) // => Promise
  .then(function (valueRes) {
    console.log(2)
    return ajax('/api/valueRes')
  }) // => Promise
  .then(function (valueResRes) {
    console.log(3333)
    console.log(valueResRes)
    return valueResRes
  }) // => Promise
  .catch(function (err) {
    console.log(err)
  })
  • Promise对象的then方法会返回一个全新的 Promise对象
  • 后面的then方法就是在为上一个then返回的 Promise注册回调
  • 前面then方法中回调函数的返回值会作为后面then方法回调的参数

Promise静态方法

Promise.resolve('foo')
// 等价于
new Promise(function (resolve, reject) {
  resolve('foo')
})

Primose并行执行

// api 返回多个接口,然后拿到地址,并行请求其余接口
// 等待所有任务执行结束,进入then
ajax('/api')
  .then(value => {
    const urls = Object.values(value)
    const tasks = urls.map(url => ajax(url))
    return Promise.all(tasks)
  })
  .then(values => {
    console.log(values)
  })

Promise.race

// Promise.race 实现500ms超时控制
const request = ajax('/api/posts.json')
const timeout = new Promise((resolve, reject) => {
  setTimeout(() => reject(new Error('timeout')), 500)
})

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

后记

promise的常用方法,本篇内容介绍的差不多了。 多个promise链式调用的执行顺序,即:宏任务、微任务。放在下一篇讲解。