# 为什么用 Promise
调用 API 如下载文件,读取文件一些平时你会执行的异步操作而不使用同步操作。因为服务器可能会性能下降,响应慢等等问题,你不希望因为等待着结果,让整个进程都被堵住。比如下面模拟远程求两个数之和:
~~~
// 远程相加两数字,调用 API 获得结果
function addAsync (num1, num2, callback) {
//使用有名的 jQuery getJSON 的回调 API
return $.get('http://www.example.com', {
num1: num1,
num2: num2
}, callback);
}
addAsync(1, 2, function(data) {
const result = data; // 这里你得到 result = 3
console.log(result);
});
~~~
这个语法看上去 OK,但想一系列的异步操作怎么办?比如说,不同于一次仅仅相加两个数字,我们希望加 3 次,即下次运算用到上次计算的结果。
~~~
let resultA, resultB, resultC;
function addAsync (num1, num2, callback) {
return $.get('http://www.example.com', {
num1: num1,
num2: num2
}, callback);
}
addAsync(1, 2, function(data) {
// callback 1
resultA = data; // you get result = 3 here
addAsync(resultA, 3, function(data1) {
// callback 2
resultB = data1; // you get result = 6 here
addAsync(resultB, 4, function(data2) {
// callback 3
resultC = data2; // you get result = 10 here
console.log('total' + resultC);
console.log(resultA, resultB, resultC);
});
});
});
~~~
这个语法很不友好。更贴切地说,这个看上去像金字塔,人们经常称呼为 "回调地狱",因为一个回调嵌在另一个回调之中。若你还觉得没有问题,可以想象有 10 个回调,这代码得嵌套 10 次!
# Promise
Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。说直白点就是 Promise 就是一种写代码的方式,并且是用来写 JavaScript 编程中的异步代码的。它由社区最早提出和实现,ES6 将其写进了语言标准,统一了用法,原生提供了 Promise 对象。
有了 Promise 对象,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。此外,Promise 对象提供统一的接口,使得控制异步操作更加容易。
一个 Promise 对象 有以下几种状态:
* pending: 初始状态,既不是成功,也不是失败状态。
* fulfilled: 意味着操作成功完成。
* rejected: 意味着操作失败。
!\[\](file://D:/H5%E5%AD%A6%E7%A7%91%E5%8D%81%E4%B9%9D%E6%9C%9F%E8%AF%BE%E7%A8%8B/nodejs-prosime07/img/promise%E7%8A%B6%E6%80%81%E5%8F%98%E5%8C%96.png?lastModify=1570810183)
# 基本用法
构造实例:
* 构造函数接受一个函数作为参数;
* 调用构造函数得到实例 p 的同时,作为参数的函数会立即执行;
* 参数函数接受两个回调函数参数 resolve 和 reject;
* 在参数函数被执行的过程中,**若在其内部调用 resolve,会将 p 的状态变成 fulfilled,或者调用 reject,会将 p 的状态变成 rejected**。
调用 then:
* 调用 then 可以为实例 p 注册两种状态回调函数;
* 当实例 p 的状态为 fulfilled,会触发第一个函数执行;
* 当实例 p 的状态为 rejected,则触发第二个函数执行。
调用 catch:
* 调用 catch 用于注册 rejected 状态的回调函数,同时该回调也是程序出错的回调,即如果前面的程序运行过程中出错,也会进入执行该回调函数。
~~~
var isMomHappy = false;
// Promise
var p = new Promise(
function (resolve, reject) {
if (isMomHappy) {
var phone = {
brand: 'Samsung',
color: 'black'
};
resolve(phone); // 完成了
} else {
var reason = new Error('妈妈不开心');
reject(reason); // reject
}
}
);
p.then(function (fulfilled) {
// 太好啦, 你获得了一个新手机
console.log(fulfilled);
// output: { brand: 'Samsung', color: 'black' }
})
.catch(function (error) {
// 好不幸,你妈妈没买手机
console.log(error.message);
// output: '妈妈不开心'
});
~~~
# 使用 Promise 改造 AJAX
~~~
let request = obj => {
return new Promise((resolve, reject) => {
let xhr = new XMLHttpRequest();
xhr.open(obj.method || "GET", obj.url);
if (obj.headers) {
Object.keys(obj.headers).forEach(key => {
xhr.setRequestHeader(key, obj.headers[key]);
});
}
xhr.onload = () => {
if (xhr.status >= 200 && xhr.status < 300) {
resolve(xhr.response);
} else {
reject(xhr.statusText);
}
};
xhr.onerror = () => reject(xhr.statusText);
xhr.send(obj.body);
});
};
request({url: "employees.json"})
.then(data => {
console.log(data);
})
.catch(error => {
console.log(error);
});
~~~