浅析异步与 Promise(二)

395 阅读2分钟

当异步任务有两个结果:成功和失败 时,怎么办?

方法一:回调接受两个参数

fs.readFile('./1.txt', (error, data) => {
  if (error) {
    console.log('失败');
    return;
  }
  console.log(data.toString());  //成功
})

方法二:使用两个回调

ajax('GET', '/1.json', data() => {}, error() => {})
//data() => {} 是成功回调,error() => {} 是失败回调

//或者:
ajax('GET', '/1.json', {
  success:()=>{}, fail:()=>{}
})
//接受一个对象,对象有两个key,表示成功和失败

这些方法的不足(这也是为什么使用 Promise 的原因):

  1. 名称不规范,有人用 success + error,有人用 success + fail,有人用 done + fail
  2. 容易出现回调地狱,代码变得看不懂
  3. 很难进行错误处理

解决回调问题(Promise 的用途):

  1. 规范回调的名字和顺序
  2. 避免回调地狱,让代码可读性更强
  3. 很方便地捕获错误

以 AJAX 的简单封装为例,未使用 Promise 前:

ajax = (method, url, options) => {
  const {success, fail} = options  //析构赋值
  const request = new XMLHttpRequest()
  request.open(method, url)
  request.onreadystatechange = () => {
    if (request.readyState === 4) {
      //成功就调用 success,失败就调用 fail
      if (request.status < 400) {
        success.call(null, request.response)
      } else if (request.status >= 400) {
        fail.call(null, request, request.status)
      }
    }
  }
  request.send()
}

ajax('get', '/xxx', {
  success(response) {}, fail: (request, status) => {}
})
//用到了两个回调,还使用了 success 和 fail

使用 Promise 的 AJAX 封装:

ajax = (method, url, options) => {
  return new Promise((resolve, reject) => {
    const request = new XMLHttpRequest()
    request.open(method, url)
    request.onreadystatechange = () => {
      if (request.readyState === 4) {
        //成功就调用 resolve,失败就调用 reject
        if (request.status < 400) {
          resolve.call(null, request.response)
        } else if (request.status >= 400) {
          reject.call(null, request)
        }
      }
    }
    request.send()
  })
}

ajax('get', '/xxx')
  .then((response) => {}, (request) => {})
//也是回调,但是不需要记 success 和 fail
//.then 的第一个参数就是 success
//.then 的第二个参数就是 fail

上面代码中,ajax() 返回了一个含有 .then() 方法的 Promise 对象。

使用 return new Promise((resolve, reject) => {...}) 就能得到这个含有 .then() 方法的对象。

总结

Promise 是前端解决异步问题的统一方案,Promise 的基本用法如下:

第一步: 定义

  • return new Promise((resolve, reject)=>{...})
  • 任务成功则调用 resolve(result)
  • 任务失败则调用 reject(error)
  • resolve 和 reject 会再去调用成功和失败函数

第二步:使用

使用 .then(success, fail) 传入成功和失败函数