Promise 简介
Promise
是ES6
的新特性,提供了对js
异步编程控制的新的解决方案,在过去书写异步代码时要靠回调函数,当异步操作依赖于其他异步操作的返回值时,会出现一种现象,被程序员们称为 “回调地狱”,即多层回调函数嵌套,这种代码的可读性、维护性都很差,因此诞生了Promise
,当然Promise
并不是完全摆脱回调,而只是改变了传递回调的位置,大大减少了回调函数嵌套。
Promise 的使用
实例方法 then
Promise
中的代码默认是同步执行的,then
方法中的回调在微任务队列中执行,在 Promise
的 then
方法中支持传入两个参数,一个是成功的回调,一个是失败的回调,在 Promise
中调用了 resolve
方法,就会在 then
中执行成功的回调,调用了 reject
方法,就会在 then
中执行失败的回调,成功的回调和失败的回调只能执行一个,resolve
和 reject
方法调用时传入的参数会传递给 then
方法中对应的回调函数。
/* 执行 resolve */
const p = new Promise((resolve, reject) => {
console.log(1);
resolve(3);
});
console.log(2);
p.then(data => {
console.log(data);
}, err => {
console.log(err);
});
// 1
// 2
// 3
/* 执行 reject */
const p = new Promise((resolve, reject) => {
reject();
});
p.then(() => {
console.log(1);
}, () => {
console.log(2);
});
// 2
如果 Promise
中发生错误,就会在 then
中执行失败的回调。
/* 失败的回调 */
const p = new Promise((resolve, reject) => {
throw new Error();
});
p.then(() => {
console.log(1);
}, () => {
console.log('报错啦');
});
// 报错啦
当同一个 Promise
实例的 then
方法多次调用时,就会多次执行。
/* 多次调用 then */
const p = new Promise((resolve, reject) => {
resolve('成功');
});
p.then(data => {
console.log(data);
});
p.then(data => {
console.log(data);
});
// 成功
// 成功
Promise
支持链式调用,每次调用一次 then
方法都会返回一个新的 Promise
实例,如果该 then
方法中执行的回调函数有返回值,并且这个返回值会作为返回的下一个 Promise
实例的 then
方法回调的参数,如果 then
方法的返回值是一个 Promise
实例,那就返回一个新的 Promise
实例,将 then
返回的 Promise
实例执行后的结果作为返回 Promise
实例回调的参数。
/* 链式调用 then */
function read(url) {
return new Promise((resolve, reject) => {
fs.readFile(url, 'utf8', (err, data) => {
if (err) reject(err);
resolve(data);
});
});
}
read('1.txt').then(data => {
// 假设此时读到的内容为 Hello world
return data;
}, err => {
console.log(err);
}).then(data => {
console.log(data);
// Hello world
}, err => {
console.log(err);
});
read('1.txt').then(data => {
// 假如此时读到的 1.txt 的内容为 2.txt 的字符串,2.txt 的内容为 Hello world
return read(data);
}, err => {
console.log(err);
}).then(data => {
console.log(data);
// Hello world
}, err => {
console.log(err);
});
在 Promise
实例的 then
中如果有错误产生,在返回的新的 Promise
实例中的 then
方法中会执行错误的回调。
/* 链式调用 then 出错 */
const p = new Promise((resolve, reject) => {
resolve();
});
p.then(() => {
console.log('success', 1);
throw new Error();
}, () => {
console.log('error', 1);
}).then(() => {
console.log('success', 2);
}, () => {
console.log('error', 2)
});
// success 1
// error 2
在 Promise 中有三个状态:
pending
:等待态fulfilled
:成功态rejected
:失败态
Promise
实例的状态只能从pending
到fulfilled
或从pending
到rejected
,状态一旦发生变化就不可逆,所以Promise
实现链式调用与jQuery
不同,返回的不是this
,只能是一个新的Promise
。
实例方法 catch
在 Promise
中实例的 catch
方法可以捕获创建 Promise
过程中和链式调用中的异常,不需要每次调用 then
方法中都传入错误的回调,在链式调用的过程中只要有任何一个 then
中出现错误,都会被 catch
方法捕获到。
/* 创建 Promise 实例时出错 */
const p = new Promise((resolve, reject) => {
throw new Error();
});
p.then(() => {
console.log('success');
}).catch(() => {
console.log('error');
});
// error
/* 调用 then 方法时出错 */
const p = new Promise((resolve, reject) => {
resolve();
});
p.then(() => {
throw new Error();
console.log('success', 1);
}).then(() => {
console.log('success', 2);
}).catch(() => {
console.log('出错了');
});
// 出错了
p.then(() => {
console.log('success', 1);
}).then(() => {
throw new Error();
console.log('success', 2);
}).catch(() => {
console.log('出错了');
});
// success 1
// 出错了
实例方法 finally
Promise
的实例方法 finally
是 ES7
标准引入的,不管最后 Promise
实例的状态是成功还是失败出传入的回调都会执行,回调函数没有参数,这说明 finally
方法里的操作与 Promise
实例状态无关,不依赖于 Promise
的执行结果。
const p = new Promise((resolve, reject) => {
resolve();
});
p.finally(() => {
console.log('执行了');
});
// 执行了
const p = new Promise((resolve, reject) => {
reject();
});
p.finally(() => {
console.log('执行了');
});
// 未使用 catch 捕获错误,finally 不执行
finally
方法执行后返回一个新的 Promise
,继续调用 then
方法,返回值为执行 finally
之前的结果。
const p = new Promise((resolve, reject) => {
resolve(1);
});
p.finally(() => {
console.log('fanilly');
}).then(data => {
console.log(data);
});
// fanilly
// 1
const p = new Promise((resolve, reject) => {
resolve(1);
});
p.then(data => {
return data;
}).finally(() => {
console.log('执行了');
return 2;
}).then(data => {
console.log('data: ', data);
});
// 执行了
// 1
// data: Promise {<resolved>: undefined}
静态方法 Promise.all
Promise
中的静态方法 all
可以实现多个 Promise
实例的并行,当所有结果都为成功时,返回一个数组,该数组存储的为每一个 Promise
实例的返回结果,每一个 Promise
实例的返回顺序先后不固定,但是返回值的数组内存储每一个 Promise
的返回值的结果按照最初传入的顺序排列,all
方法的返回值为一个新的 Promise
实例,返回的数组作为返回新 Promise
的 then
方法成功回调的参数。
当 all
传入的参数数组中的 Promise
实例执行时,只要有一个失败,则直接返回该 Promise
实例失败的结果或错误信息。
/* Promise.all 方法 */
const p1 = new Promise((resolve, reject) => {
resolve(1);
});
const p2 = new Promise((resolve, reject) => {
resolve(2);
});
Promise.all([p1, p2]).then(data => {
console.log(data);
});
// [1, 2]
/* Promise.all 错误捕获 */
const p1 = new Promise((resolve, reject) => {
resolve(1);
});
const p2 = new Promise((resolve, reject) => {
reject(2);
});
Promise.all([p1, p2]).then(data => {
console.log(data);
}).catch(err => {
console.log(err);
});
// 2
静态方法 Promise.race
Promise
的静态方法 race
的用法和 all
类似,参数同为一个存储 Promise
实例的数组,返回值同样是一个新的 Promise
的实例,不同的是,数组中的 Promise
实例只有一个结果为成功,那就直接返回这个结果(只取出最快返回的结果),在没有成功的结果之前有一个出错,就直接返回这个错误。
/* Promise.race 方法 */
const p1 = new Promise((resolve, reject) => {
setTimeout(() => resolve(1), 2000);
});
const p2 = new Promise((resolve, reject) => {
setTimeout(() => resolve(2), 1000);
});
Promise.race([p1, p2]).then(data => {
console.log(data);
}).catch(err => {
console.log(err);
});
// 2
/* Promise.race 错误捕获 */
const p1 = new Promise((resolve, reject) => {
setTimeout(() => reject(1), 1000);
});
const p2 = new Promise((resolve, reject) => {
setTimeout(() => resolve(2), 2000);
});
Promise.race([p1, p2]).then(data => {
console.log(data);
}).catch(err => {
console.log(err);
});
// 1
静态方法 Promise.resolve
Promise
的静态方法 resolve
可以直接将 Promise
的状态变为成功并返回一个新的 Promise
实例,resolve
的参数会传递给返回的新 Promise
实例 then
中成功回调。
/* Promise.resolve 方法 */
Promise.resolve('hello').then(data => {
console.log(data);
});
// hello
静态方法 Promise.reject
Promise
的静态方法 reject
与 resolve
使用完全相同,都返回一个新的 Promise
实例,不同的是 reject
的参数会传递给新 Promise
实例的 then
方法失败回调。
/* Promise.reject 方法 */
Promise.reject('出错了').then(null, err => {
console.log(err);
});
// 出错了
当成功的回调不传递时,可以使用 null
代替,因为 null
作为参数会被忽略掉,将参数穿透到下一个 then
的回调中。
总结
Promise
是异步编程的一大趋势,也是当前更先进的异步解决方案的基础,下一篇我们着重讨论一下Promise
的实现原理以及A+
规范。