所有异步编程的根基都是回调函数

回调函数可以理解成一件你想要做的事情。
也就是说,由调用者定义,交给执行者执行的函数就叫做回调函数

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

如果我们使用传统的回调方式去完成复杂的异步流程,就会无法避免的遭遇一个传统的问题:回调地狱。CommonJS为了避免这个问题,提供了一个Promise方案。

Promise基本用法

Promise实例:

// Promise 基本示例

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");

Promise使用案例(ajax by Promise)

// Promise 方式的 AJAX

function ajax(url) {
  return new Promise(function (resolve, reject) {
    var xhr = new XMLHttpRequest();
    xhr.open("GET", url);
    // 请求完成之后直接拿到JSON文件
    xhr.responseType = "json";
    xhr.onload = function () {
      if (this.status === 200) {
        resolve(this.response);
      } else {
        reject(new Error(this.statusText));
      }
    };
    xhr.send();
  });
}

// 提前准备好的JSON文件
ajax("/api/users.json").then(
  function (res) {
    console.log(res);
  },
  function (error) {
    console.log(error);
  }
);

Promise常见的误区

嵌套使用的方式是Promise最常见的误区
错误例:

// Promise 常见误区

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) {

//         })
//       })
//     })
//   })
// })

正确例(链式调用)

// Promise 链式调用

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();
  });
}

// then 方法返回的也是一个Promise对象
// 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 (返回的是一个全新的Promise对象)
//目的是实现一个Promise的链条

ajax("/api/users.json")
  .then(function (value) {
    console.log(1111);
    return ajax("/api/urls.json");
  }) // => Promise
  .then(function (value) {
    console.log(2222);
    console.log(value);
    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) {
    console.log(5555);
    console.log(value);
  });

总结:
1.Promise的then方法可以返回一个全新的Promise对象,我们可以使用链式调用的方法添加then方法
2.后面的then方法实际上就是为上一个then方法返回的Promise注册回调
3.前面then回调函数的返回值会作为后面then方法回调的参数
4.如果我们前面回调返回的是一个Promise的话,后面的then方法回调会等待这个Promise结束

Promise错误捕获

有四种种写法:
then的第二个参数:

// 同时注册的 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)
  })

链接调用的catch() 更加通用

// 因为 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
);

Nodejs全局捕获异常

// Node.js 中使用以下方式
 process.on('unhandledRejection', (reason, promise) => {
   console.log(reason, promise)
   // reason => Promise 失败原因,一般是一个错误对象
   // promise => 出现异常的 Promise 对象
 })

全局捕获的方式并不是一件值得推崇的事,更好的做法是在代码中明确的捕获每一个可能发生的异常

Promise静态写法

Promise.resolve(value)

把一个值快速的转变成一个Promise对象

Promise.resolve('foo')
  .then(function (value) {
    console.log(value)
  })

这段代码基本等价于:

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

值得注意的是:如果使用这个方法包装一个Promise,那么将会原路返回这个Promise对象

// 如果传入的是一个 Promise 对象,Promise.resolve 方法原样返回

var promise = ajax('/api/users.json')
var promise2 = Promise.resolve(promise)
console.log(promise === promise2) // true

有一个特殊情况:

// 如果传入的是带有一个跟 Promise 一样的 then 方法的对象,
// Promise.resolve 会将这个对象作为 Promise 执行

Promise.resolve({
  then: function (onFulfilled, onRejected) {
    onFulfilled('foo')
  }
})
.then(function (value) {
  console.log(value) // foo
})

可以说是实现了thenable的接口,可以被then的对象,支持这个的原因在于,在Promise原生对象没有普及之前,很多的时间我们都是使用的第三方库实现的Promise,在现在的环境下,要将这种三方的Promise转为原生的Promise就可以用到这个方法。

Promise.reject()

// Promise.reject 传入任何值,都会作为这个 Promise 失败的理由

// Promise.reject(new Error('rejected'))
//   .catch(function (error) {
//     console.log(error)
//   })

Promise.reject("anything").catch(function (error) {
  console.log(error);
});
最后修改:2023 年 07 月 27 日
收款不要了,给孩子补充点点赞数吧