JavaScript中 Promise 的理解
Promise 是什么?
Promise 是 ES6 提供的一种异步编程解决方案,解决了传统异步处理的回调金字塔问题; Promise 对象是一个构造函数,用来生成 Promise 实例;
Promise 构成:console.dir(Promise);
可以看出,Promise 这个构造函数自己身上有 resolve(), reject(), all(), race() 这几种常见的方法,原型上有 catch(), then(), finally() 常用的方法,所以 Promise 的实例可以使用 catch(), then() 等方法;
Promise 的用法
- Promise 构造函数接收一个函数作为参数,这个函数有两个参数,这两个参数都是函数,由 js引擎提供;
// 传递一个函数作为实例化参数 const promise = new Promise(function(resolve, reject) { if (/* 异步操作成功 */) { // 改变状态为 fulfilled resolve(value); } else { // 改变状态为 rejected reject(error); } });
- resolve 函数的作用是将 Promise 对象的状态从 "待定"(pending) 改变为 "成功"(fulfilled), 在异步操作成功的时候调用,并将异步操作的结果作为参数传递出去;
reject 函数的作用是将 Promise 对象的状态从 "待定"(pending) 改变为 "失败"(rejected), 在异步失败的的时候调用,并将异步操作报的错误作为参数传递出去; - 可以使用 Promise 生成的实例 promise 调用 then() 分别指定 fulfilled状态 和 rejected状态 的回调方法;
/** * promise 实例的then方法,接收两个参数,这两个参数都是函数 * 第一个参数:Promise对象的状态变为 resolved 状态时调用 * 第二个参数(可选的):Promise对象的状态变为 rejected 状态时调用 */ promise.then(function(value) { // value 为 Promise 对象实例化时调用resolve(...)方法传入的值 // success code }, function(error) { // error 为 Promise 对象实例化时调用reject(...)方法传入的错误 // failure code });
Promise 链式写法
const promise = new Promise(function(resolve, reject) {
// 获取一个10 以内的正整数
const num = Math.floor(Math.random()*10);
if (num > 5) {
resolve("num 大于 5");
} else {
reject("num 小于 5")
}
});
// 第一个 then
promise.then(function(value) {
// 状态由 pending 变为 fulfilled 的回调
// 这里 value 的值为 (num 大于 5)
return "成功了";
}, function(error) {
// 状态由 pending 变为 rejected 的回调
// 这里 error 的值为 (num 小于 5)
})
// 第二个 then
.then(function(value) {
// 这里 value 的值为 上一个 fulfilled 状态回调的返回值(成功了)
console.log(value); // 如果 num > 5 打印 成功了
});
实例化一个 Promise,参数为一个函数,函数体实现随机生成一个10以内的正整数,如果 num > 5
将 Promise 状态改为 fulfilled 状态,并向下传递了一个 "num 大于 5"
的字符串,反之,将 Promise 状态改为 rejected 状态,向下传递了一个 "num 小于 5"
的字符串;
当第一个 promise 的成功回调里返回 "成功了" 时,第二个 promise 的成功回调的参数就是 "成功了";
由此可以看出,第一个 promise 不管成功回调还是失败回调,他的返回值作为第二个 promise 中的成功时回调函数的参数值;
链式写法能一直then下去的原因:链式调用靠的是返回新的 promise,来保证可以一直走成功或失败;
Promise.resovle
返回一个 Promise 实例,这个实例处于 resolve 状态
根据传入的参数不同有不同返回值:
- 值(对象, 数组, 字符串等):作为 resolve 传递出去的值;
- Promise 实例:原封不动返回;
// 返回一个立刻成功的promise
// 别人提供 给你一个方法,需要你传入一个promise,但你只有一个普通的值,
// 你就可以通过这个方法把这个普通的值(string number object)转成一个promise对象
Promise.resolve = function(value){
return new Promise(function(resolve){
resolve(value);
});
}
示例:
const promise = Promise.resolve(66);
promise.then(function(resolve) {
// 值为调用 .resolve() 传入的参数
console.log(resolve); // 66
});
// 值为 promise 实例原样返回
console.log(promise === Promise.resolve(promise)); // true
Promise.reject
返回一个 Promise 实例,这个实例处于 reject 状态。
参数一般就是抛出的错误信息;
//返回一个立刻失败的promise
Promise.reject = function(reason){
return new Promise(function(resolve,reject){
reject(reason);
});
}
示例:
const promise = Promise.reject("出错啦");
promise.then(null, function(error) {
// 值为调用 .reject() 传入的参数
console.log(error); // 出错啦
});
Promise.catch
Promise.prototype.catch
方法是 .then(null, rejection)
的别名,用于指定发生错误时的回调函数;
//catch原理就是只传失败的回调
Promise.prototype.catch = function(onRejected){
this.then(null,onRejected);
}
示例:
const promise = Promise.reject("出错啦");
// 和 then() 的第二个参数一样,用来指定 reject 的回调
promise.catch(function(catchError) {
// 值为调用 .reject() 传入的参数
console.log(catchError); // 出错啦
});
// 另一种用法:在执行 resolve 的回调(then 中的第一个参数)时,
// 如果抛出异常了(代码出错了),并不会报错卡死 js,而是会进到这个 catch 方法中
const exceptionPromise = new Promise(function(resolve, reject) {
resolve("成功了");
});
exceptionPromise.then(function(value) {
console.log(value);
// exception 在当前位置未定义
return exception;
}).catch(function(catchError) {
// 在执行 resolve 的回调时,代码中存在语法错误,
// 此时 catchError 值为抛出的异常信息
console.log(catchError); // ReferenceError: exception is not defined
});
// 同理 reject 回调也是如此;
Promise.all
参数:接受一个iterable(Array,Map,Set),iterable内都是Promise实例
返回值:返回一个 Promise 实例,这个 Promise 实例的状态转移取决于参数的 Promise 实例的状态变化。当参数中所有的实例都处于 resolve 状态时,返回的 Promise 实例会变为 resolve 状态。如果参数中任意一个实例处于 reject 状态,返回的 Promise 实例变为 reject 状态;
Promise.all = function(promises){
return new Promise(function(resolve,reject){
let done = gen(promises.length,resolve);
for(let i=0;i<promises.length;i++){
promises[i].then(function(data){
done(i,data);
},reject);
}
});
}
示例:
const promise1 = new Promise(function(resolve, reject) {
setTimeout(function() {
resolve("promise1 success")
}, 1000);
});
const promise2 = new Promise(function(resolve, reject) {
setTimeout(function() {
resolve("promise2 success")
}, 2000);
});
const promise3 = new Promise(function(resolve, reject) {
setTimeout(function() {
resolve("promise3 success")
}, 3000);
});
// 传入由三个 promise 组成的数组
Promise.all([promise1, promise2, promise3])
.then(function(resolves) {
// 三秒后输出以下结果(数组中的Promise全部完成,由最慢的决定)
// ['promise1 success', 'promise2 success', 'promise3 success']
console.log(resolves);
});
Promise.race
参数:接受一个iterable(Array,Map,Set),iterable内都是 Promise 实例
返回值:返回一个 Promise 实例,这个 Promise 实例的状态转移取决于参数的 Promise 实例的状态变化。当参数中任何一个实例处于 resolve 状态时,返回的 Promise 实例会变为 resolve 状态。如果参数中任意一个实例处于 reject 状态,返回的 Promise 实例变为 reject 状态;
Promise.race = function(promises){
return new Promise(function(resolve,reject){
for(let i=0;i<promises.length;i++){
promises[i].then(resolve,reject);
}
});
}
示例:
const promise1 = new Promise(function(resolve, reject) {
setTimeout(function() {
resolve("promise1 success")
}, 1000);
});
const promise2 = new Promise(function(resolve, reject) {
setTimeout(function() {
resolve("promise2 success")
}, 2000);
});
const promise3 = new Promise(function(resolve, reject) {
setTimeout(function() {
resolve("promise3 success")
}, 3000);
});
// 传入由三个 promise 组成的数组
Promise.race([promise1, promise2, promise3])
.then(function(resolves) {
// 1秒后输出以下结果(谁先完成结果就是谁,由最快的决定)
// promise1 success
console.log(resolves);
});
总结
Promise 对象有三种状态,并且不受外界影响,Promise 的状态一旦改变就不会再变,并且状态不可逆,只能从 pending(待定) 改变成 fulfilled(成功) 或 pending(待定) 改变成 rejected(失败);
- pending(待定)
- fulfilled(成功)
- rejected(失败)
正是因为这些特点导致 Promise 有三个缺点:
- 无法取消 Promise,一旦新建就会立即执行,中途无法取消;
- 如果没有设置回调函数,Promise 内部抛出的错误,不会反映到外部;
- 当 Promise 处于 pending 状态时,无法得知目前进展到哪一个阶段,是刚刚开始还是即将完成;