# Promise 对象

JavaScript 是一个单线程的编程语言,通过异步、回调函数来处理各种事件。因此,如果要处理多个有先后顺序的事件,那么将会出现多次嵌套回调函数的情况,这也被很多开发人员称为『回调地狱』。

Promise 对象则是通过将代表异步操作最终完成或者失败的操作封装到了对象中。Promise 本质上是一个绑定了回调的对象,不过这样可以适当缓解多层回调函数的问题。

Promise 最早由社区提出和实现,随后被 ES6 将其纳入标准中。

Promise 规定每个 Promise 对象都有『状态』,Promise 对象的初始状态是 pending ,接下来,它可以变成 fulfilled(代表成功)或者 rejected(代表失败)

pending 
│──> fulfilled
└──> rejected

Promise 对象的状态的变化只会发生一次,且不可逆。一旦 Promise 对象的状态一旦变为 2 种终止态中的任意一个,都称它为『已定型』(resolved)。

首先要认清最基本的用法。一般学习 Promise 看到的第一段代码是这样:

/**
 * 同步调用实例
 */
let p = new Promise((resolve, reject) => {
  // 做一些事情,根据你自己的逻辑来。通常是异步逻辑

  // 根据上面代码的执行结果,可能调用 resolve,也可能调用 reject
  if (/* 条件随便写 ^_^ 根据你自己的逻辑来 */) {
    resolve();
  } else {
    reject();
  }
});

p.then(() => {
    // 传给 Promise 的 resolve 回调。一旦它被执行,则意味着 Promise 对象进入成功状态。
}, () => {
    // 传给 Promise 的 reject 回调。一旦它被执行,则意味着 Promise 对象进入失败状态。
})


/*
p.then(函数一, 函数二);

也可以写成:

p.then(函数一)
 .catch(函数二);
*/

通过以上例子,我们应该(或者需要知道)以下基本概念(只考虑 Promise 中为异步代码)

  • Promise 是一个构造函数,使用 new Promise 创建实例。所以变量 p 引用的是创建出来的一个对象。

  • Promise 构造函数有一个参数 executor ,它是一个函数,在 Promise 创建实例的过程中就会被执行

  • executor 函数有两个参数 resolvereject ,也都是函数。在 executor 执行的过程中,会触发 resolve、reject 的执行(以上例子中为异步执行)

  • Promise 中有两个属性:value 和 reason 。resolve 执行时可以传入异步事件成功回调执行的结果,这个结果改变 Promise 的 value 。

    • resolve 执行时会执行 then 传入的 onFulfilled ,参数为 Promise 的 value 。类似,reject 执行时可以传入异步事件失败回调执行的结果,这个结果改变 Promise 的 reason 。
    • reject 执行时会执行 then 传入的 onRejected ,参数为 Promise 的 reason 。
  • Promise 实例有 then 方法,then 方法有两个参数,Promise 被解决的回调 onFulfilled、Promise 被拒绝的回调 onRejected,都是函数。

    • onFulfilled 被传入 Promise 的 onFulfilledCallbacks 队列,供 resolve 执行时调用。
    • onRejected 被传入 Promise 的 onRejectedCallbacks 队列,供 reject 执行时调用。
  • onFulfilled 有一个参数 value ,在实际调用时由 resolve 函数传递。类似,onRejected 有一个参数 reason,在实际调用时由 reject 函数传递。

在 Promise 中,最值得注意的是:resolve() 是异步执行,它是一个微任务。

用 Promise 包装 JQuery 的 Ajax 请求:

function ajax(params) {
    return new Promise(function (resolve, reject) {
        $.ajax({
            url: params && params.url || '',
            type: params && params.type || 'post',
            contentType: params && params.contentType || 'application/x-www-form-urlencoded',
            data: params && params.data || '',
            success: function (res) {
                resolve(res)
            },
            error: function (res) {
                reject(res);
            }
        });
    });
}

// 测试
ajax({
    url: '/departments',
    type: "get",
}).then((res) => {
        console.log(res);
    }
).catch((res) => {
    alert(res);
});