ES6学习之更优的异步编程方案 Promise

本文深入讲解Promise对象的创建、状态转换、链式调用、异常处理等核心概念,通过实例演示Promise的执行流程与特性,帮助读者掌握Promise的高级用法。

Promise译为“承诺”。
用于向执行者(Promise实例)交待一件任务,当任务结束,应如何处理。

使用

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

创建promise实例需传入一个函数,函数定义了兑现承诺的逻辑(如何处理)。
函数中的参数定义两个结果的调用:

  1. 变更任务状态为Fulfilled成功或Rejected失败。
  2. 将信息作为参数传入,成功信息、失败原因等,使回调方法可以接收:成功被then接收,失败被catch接收。

状态

三种状态:pending(初始状态),fulfilled(resolved)(成功),rejected(失败)
promise对象创建即执行传入的函数,执行函数中的回调即变更promise的状态

打印Promise对象:Promise {<状态>[: 回调传参]}

// pending
var p1 = new Promise(function(resolve, reject){
	setTimeout((
		  resolve("success")
	)=>{},500);
});
console.log(p1); // Promise {<pending>}

// resolved/fulfilled
var p2 = new Promise(function(resolve, reject){
	  resolve("success")
});
console.log(p2); // Promise {<resolved>:"success"}

// rejected
var p3 = new Promise(function(resolve, reject){
	  resolve("success")
});
console.log(p3); // Promise {<rejected>:"success"}

状态不可逆

状态变更只能是pending->resolved或pending->rejected
状态一旦变更,执行回调不会生效。

var p1 = new Promise(function(resolve, reject){
	  reject("error"); // 状态变更
	  reject("error2"); // 未生效
	  resolve("success"); // 未生效
});
console.log(p1); // Promise {<rejected>:"error"}
p1.catch(err=>{
	console.log(err); // error
})

执行顺序特点

  1. 一旦新建就会立即执行
  2. 在 JavaScript 事件队列的当前运行完成之前,(resolve, reject)回调函数永远不会被调用。
var p1 = new Promise(function(resolve, reject){
	console.log(1);
	resolve("success");
	console.log(2);
});
console.log(3)
p1.then(data=>{
	console.log(4)
})
console.log(5)
setTime(()=>{
	console.log(6)
},50);
// 输出顺序:1 2 3 5 4 6

then()

then返回一个新的promise对象

var p1 = new Promise((resolve,reject) => {
	setTimeout(()=>{
		resolve('success');
	},1000);
})
var p2 = p1.then(() => {
	throw new Error('this is an error');
})
p2.catch(err=>{console.log(err.message);});
console.log('p1',p1)
console.log('p2',p2)
setTimeout(() => {
	console.log('p1',p1)
	console.log('p2',p2)
},2000);
/*输出
p1 Promise {<pending>}
p2 Promise {<pending>}
this is an error
p1 Promise {<resolved>:"success">}
p2 Promise {<rejected>:Error:this is an error>}
*/

链式调用

var p1 = new Promise((resolve,reject)=>{
	resolve(1);
});
p1.then(value=>{
	console.log(value); // 输出:1
	return value*2; // 作为下一次调用then回调的参数
}).then(value=>{
	console.log(value); // 输出:2
}).then(value=>{ // 上一次then未return,所以参数为undefined
	console.log(value); // 输出:undefined
	return Promise.resolve('resolve'); // 返回一个状态为resolved的promise,且resolve回调传参为resolve
	/* 类似于
	return new Promise((resolve,reject)=>{
		resolve('resolve');
	})
	*/
}).then(value=>{
	console.log(value); // 输出:resolve
	return Promise.reject('reject');
}).then(value=>{ // 由于上一次then返回的是状态为reject的promise,所以直接触发catch
	console.log(value); // 不会执行
}).catch(error=>{
	console.log(error); // 输出:reject
})

异常

  • 创建及操作中的异常,如果定义了catch,就会执行catch回调,否则会抛出错误。
  • Promise 对象的错误具有"冒泡"性质,会一直向后传递,直到被捕获为止。也就是说,错误总是会被下一个 catch 语句捕获。
  • 错误处理完,会继续执行后面的操作,并将catch的return作为下次函数执行的参数。

定义:以下几种定义异常回调在处理创建promise异常时效果一样。注意catch只会处理上一次操作的错误。比如.then(func1,catch1).then(func2,catch2)中func1中的错误由catch2处理,catch1不会触发。

var p1 = new Promise((resolve,reject)=>{
	foo.bar();
	resolve('success');
});
var catchFunc = err=>{};
p1.then(null, catchFunc);
p1.then(()=>{}, catchFunc);
p1.then(()=>{}).then(()=>{}).catch(catchFunc);
p1.then(()=>{}).then(()=>{},catchFunc);
p1.catch(catchFunc);

触发

  1. 执行代码中的异常会触发下次then的第二个函数参数(reject)或catch。例如上例的foo.bar()
  2. 创建promise时状态变更为rejected时会触发。
  3. 在创建promise时执行的代码有错误,类似执行了reject(err.message)。若错误在状态变更前,则直接变更promise的状态为rejected,并且如果未定义reject回调(catch)就会抛出错误;若在状态变更后,虽然会中断后面代码的执行,由于状态已变更,则reject(err.message)不会生效,所以也不会抛出错误。

处理

  1. 异常处理完,then/catch返回的promise对象会继续执行链式调用
  2. 若由代码错误导致的异常,导致执行中断

示例1:

var p1 = new Promise((resolve,reject)=>{
	foo.bar();
	resolve('success');
});
console.log(p1);
/*输出:
Promise {<rejected>: ReferenceError: foo is not defined}
*/
/*抛出错误:
Uncaught (in promise) ReferenceError: foo is not defined
*/

var p1 = new Promise((resolve,reject)=>{
	foo.bar();
	resolve('success');
});
p1.catch(err=>{
	console.log(err.message);
})
console.log(p1);
/*定义了catch,不会抛出错误,输出:
Promise {<rejected>: ReferenceError: foo is not defined}
ReferenceError: foo is not defined
*/
var p1 = new Promise((resolve,reject)=>{
	resolve('success');
	foo.bar();
});
console.log(p1);
/*状态已变更,后面的异常不会生效,也不会抛出错误。输出:
Promise {<resolved>: "success"}
*/

示例2:

由于创建promise代码中报错,导致两件事情:

  1. promise状态变更为rejected,类似执行了 reject(err.message)
  2. 代码中断,未执行后面的代码
var p1 = new Promise((resolve,reject)=>{
	foo.bar();
	console.log(1);
	resolve('success');
	console.log(2);
});
p1.then(value=>{
	console.log(value);
}).catch(err=>{
	console.log(err.message);
	console.log(p1);
});
/* 输出
ReferenceError: foo is not defined
Promise {<rejected>: ReferenceError: foo is not defined}
*/

示例3:

错误具有冒泡性质,总会被下一个catch捕获
错误处理完会继续执行后面的then

var p1 = new Promise((resolve,reject)=>{
	foo.bar();
	resolve('success');
});
p1.then(value=>{
	console.log('then1:' + value);
},err=>{
	console.log('catch1_error:'+err.message);
	return 'catch1_return';
}).catch(err=>{
	console.log('catch2_error:'+err.message);
	return 'catch2_return';
}).then(value=>{
	// then2_foo.bar(); // 若在此处执行错误代码,则会中断后面代码的执行
	console.log('then2:' + value);
	then2_foo.bar();
}, err=>{
	console.log('catch3_error:'+err.message)
}).catch(err=>{
	console.log('catch4_error:'+err.message);
});
/* 输出
catch1_error:ReferenceError: foo is not defined
then2:catch1_return
catch4_error:ReferenceError: then2_foo is not defined
*/

then回调异步性

p1.then(
  function(value){
    console.log('p1 then1');
  }
).then(
  function(value){
    console.log('p1 then2');
  }
).then(
  function(value){
    console.log('p1 then3');
  }
);

var p2 = new Promise(function(resolve,reject){
  resolve();	
});

p2.then(
  function(value){
    console.log('p2 then1');
  }
).then(
  function(value){
    console.log('p2 then2');
  }
).then(
  function(value){
    console.log('p2 then3');
  }
);
/*输出:
p1 then1
p2 then1
p1 then2
p2 then2
p1 then3
p2 then3
*/

拆箱

Promise.resolve(data)
data如果是正常的值(字符串等),Promise.resolve会返回一个以data为参数的状态为resolved的promise对象。
data如果是一个promise对象,就会拆箱获取对这个对象的状态和值(拆箱过程是异步的),最终返回状态为resolved或rejected的对象。

Promise.reject(data)
不会进行拆箱操作,总是会返回一个状态为rejected的promise对象,并将data作为参数调用。

var p0 = new Promise((resolve,reject)=>{
	resolve(Promise.resolve('resolve'));
});
var p1 = new Promise((resolve,reject)=>{
	resolve(Promise.resolve(p0));
});
var p2 = new Promise((resolve,reject)=>{
	resolve(Promise.reject('reject'));
});
var p3 = new Promise((resolve,reject)=>{
	reject(Promise.resolve('resolve'));
});
p0.then(value => {
	console.log('p0_resolved:' + value);
}, err => {
	console.log('p0_rejected:' + err);
})
p1.then(value => {
	console.log('p1_resolved:' + value);
}, err => {
	console.log('p1_rejected:' + err);
})
p2.then(value => {
	console.log('p2_resolved:' + value);
}, err => {
	console.log('p2_rejected:' + err);
})
p3.then(value => {
	console.log('p3_resolved:' + value);
}, err => {
	console.log('p3_rejected:' + err);
})
/*输出:
p3_rejected:[Object:Promise]
p1_resolved:resolve
p2_rejected:reject
*/

p0 p1 p2中resolve需要拆箱,p3中的reject不需要拆箱,所以p3最先输出
p1中Promise.resolve(p0)依然需要拆箱,所以p0 p2先输出

其他

  1. .then 或 .catch 返回的值不能是 promise 本身,否则会造成死循环
  2. .then 或者 .catch 的参数期望是函数,传入非函数则会发生值穿透。

参考

八段代码彻底掌握 Promise
JavaScript Promise 对象
Promise 必知必会(十道题)

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值