ES6 promise

84 阅读7分钟

前言

Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。

Promise对象有以下两个特点。

1.对象里面的状态不受外界影响

2.一旦状态改变就不会在变,任何时候都可以得到这个结果

resolved()统一只指fulfilled状态 ,不包含rejected状态

有了Promise对象,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数

// promise的缺点
1. Promise也有一些缺点。首先,无法取消Promise 一旦新建它就会立即执行,无法中途取消
2.如果不设置回调函数,内部抛出的异常不会反应到外部
3.当处于pending状态时,无法得知目前紧张到什么阶段

.Promise().then(()=>{})

const promise = new Promise(function(resolve, reject) {
  // ... some code

  if (/* 异步操作成功 */){
    resolve(value);
  } else {
    reject(error);
  }
});

  const promise = files.map((item, index)=>{        
    var formData = new FormData(); // formData的格式处理图片数据        
    formData.append("file", item)         
    return new Promise((resolve, reject)=>{          
        handleUpload(formData).then(res=>{            
            resolve(res)          
        }).catch(err=>{            
            reject(err)          
        })        
    })      
 })      
 const {data} = await Promise.all(promise)      
 if(data)console.log(data) 

.Promise.all()

Promise.all()``方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。

只有promise里面的请求全都成功 才会resolve 否则就会reject

promise发请求 是多个请求一起发送 不是发送完一个再发下一个请求

Promise.all获得的成功结果的数组里面的数据顺序和Promise.all接收到的数组顺序是一致的,即p1的结果在前,即便p1的结果获取的比p2要晚。这带来了一个绝大的好处:在前端开发请求数据的过程中,偶尔会遇到发送多个请求并根据请求顺序获取和使用数据的场景,使用Promise.all毫无疑问可以解决这个问题。

 let p1 = new Promise()
 let p2 = new Promise()
 let p3 = new Promise()
Promise.all([p1, p2]).then((result) => {   
    console.log(result); // ['成功了', 'success']
}).catch((error) => {   
    console.log(error) ; // 此时,该步没有走
})
Promise.all([p1,p3,p2]).then((result) => {   
    console.log(result) ; // 此时,该步没有走
}).catch((error) => {   
    console.log(error); // 失败
})

**Promse.all在处理多个异步处理时非常有用,比如说一个页面上需要等两个或多个ajax的数据回来以后才正常显示,在此之前只显示loading图标。**

let wake = (time) => {   return new Promise((resolve, reject) => {      setTimeout(() => {         resolve(`${time / 1000}秒后醒来`)      }, time)   })};let p1 = wake(3000);let p2 = wake(2000);Promise.all([p1, p2]).then((result) => {   console.log(result); // [ '3秒后醒来', '2秒后醒来' ]}).catch((error) => {   console.log(error); // 此时,该步没有走})

.Promise.race()

Promise.race()``方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例。

Promse.race就是赛跑的意思,Promise.race([p1, p2, p3])里面哪个结果获得的快,就返回那个结果,不管结果本身是成功状态还是失败状态。

let p1 = new Promise((resolve, reject) => {   setTimeout(() => {      resolve('success');   },1000)});let p2 = new Promise((resolve, reject) => {   setTimeout(() => {      reject('failed') ;   }, 500)}) ;Promise.race([p1, p2]).then((result) => {   console.log(result); // 此时,该步没有走}).catch((error) => {   console.log(error); // failed})

.Promise.prototype

Promise.prototype.then()

Promise 实例具有then方法,也就是说,then方法是定义在原型对象Promise.prototype上的。它的作用是为 Promise 实例添加状态改变时的回调函数。前面说过,then方法的第一个参数是resolved状态的回调函数,第二个参数是rejected状态的回调函数,它们都是可选的。

then``方法返回的是一个新的Promise实例(注意,**不是原来那个Promise实例**)。因此可以采用链式写法,即then方法后面再调用另一个then方法。

getJSON("/posts.json").then(function(json) {
  return json.post;
}).then(function(post) {
  // ...
}); 
// 使用then方法,依次指定了两个回调函数。第一个回调函数完成以后,
// 会将返回结果作为参数,传入第二个回调函数。

采用链式的then,可以指定一组按照次序调用的回调函数。这时,前一个回调函数,有可能返回的还是一个Promise对象(即有异步操作),这时后一个回调函数,就会等待该Promise对象的状态发生变化,才会被调用。

getJSON("/post/1.json").then(function(post) {
  return getJSON(post.commentURL);
}).then(function (comments) {
  console.log("resolved: ", comments);
}, function (err){
  console.log("rejected: ", err);
});
// 上面代码中,第一个then方法指定的回调函数,返回的是另一个Promise对象。这时, 第二个then方法指定的回调函数,就会等待这个新的Promise对象状态发生变化。如果变为resolved,就调用第一个回调函数,如果状态变为rejected,就调用第二个回调函数。

简写成es6的形式更简单一点

getJSON("/post/1.json").then(
  post => getJSON(post.commentURL)
).then(
  comments => console.log("resolved: ", comments),
  err => console.log("rejected: ", err)
);

.Promise.any()

概念

参数:接收一个可迭代的对象( iterable),(比如:Array, Map, Set都属于ES6的iterable类型)

返回值:如果传入的参数是一个空的可迭代对象,则返回一个 已失败(already rejected) 状态的 Promise。

成功(Fulfillment)

当任何一个被传入的 promise 成功的时候,无论其他的 promises 成功还是失败,此函数会将那个成功的 promise 作为返回值 。

如果传入的参数是一个空的可迭代对象,这个方法将会同步返回一个已经完成的promise。

如果传入的任何一个 promise 已成功,或者传入的参数不包括任何 promise, 那么 Promise.any 返回一个异步成功的 promise。

失败/拒绝(Rejection)

如果所有传入的 promises 都失败,Promise.any 将返回异步失败,和一个 AggregateError 对象,它继承自 Error,有一个 errors 属性,属性值是由所有失败值填充的数组。

注意:该方法依然是实验性的,尚未被所有的浏览器完全支持。它当前处于 TC39 第四阶段草案(Stage 4)****

**用法:**

function requset() {
	return new Promise((resolve, reject) => {
		setTimeout(() => {
			resolve('请求成功')
		}, 100)
	})
}

function requset1() {
	const p = new Promise((resolve, reject) => {
		setTimeout(() => {
			resolve('请求失败')
		}, 300)
	})
	return p
}

async function getData1() {
	const promises = [requset(), requset1()];
	const results = await Promise.any(promises);
	console.log(results, '2')
}
getData1()

当输入 promises 中的第一个 promise 被执行完成时,results 会立即解析为该 promise 的值; 先完成先返回

function requset() {
	return new Promise((resolve, reject) => {
		setTimeout(() => {
			reject('请求成功')
		}, 100)
	})
}

function requset1() {
	const p = new Promise((resolve, reject) => {
		setTimeout(() => {
			resolve('请求失败')
		}, 300)
	})
	return p
}

async function getData1() {
	const promises = [requset(), requset1()];
	const results = await Promise.any(promises);
	console.log(results, '2')
}
getData1()

Promise.any() 返回的 promise 与任何第一个执行的 promise 一起执行。即使某些 promise 被 rejected,这些 rejections 也将会被忽略。

**如果输入数组中的所有 promises 都被拒绝,或者输入数组为空,那么 Promise.any() 会 rejected 包含输入的 promises 执行的 rejection 错误原因集合。**

Promise.myany = function (args) {
	let index = 0;
	let errCount = 0; //记录失败的promise执行次数
	return new Promise((resolve, reject) => {
		// 利用for of 对可迭代对象进行遍历
		for (let i of args) {
			index++;
			Promise.resolve(i).then(
				value => {
					resolve(value)
				},
				err => {
					errCount++;
					if (errCount >= index) {
						reject(new AggregateError([], 'All promises were rejected'))
					}
				}
			)
		}
	})
}

.Promise.resolve()

Promise.resolve('foo')
// 等价于
new Promise(resolve => resolve('foo')) const p = Promise.resolve();

 p.then(function () {
   // ...
 });

需要注意的是,立即`resolve()`的 Promise 对象,是在本轮“事件循环”(event loop)的结束时执行,而不是在下一轮“事件循环”的开始时。

setTimeout(function () {
  console.log('three');
}, 0);

Promise.resolve().then(function () {
  console.log('two');
});

console.log('one');

// one
// two
// three

参数是一个thenable对象,thenable对象指的是具有then方法的对象

let thenable = {
  then: function(resolve, reject) {
    resolve(42);
  }
};
// Promise.resolve()方法会将这个对象转为 Promise 对象,然后就立即执行thenable对象的then()方法let p1 = Promise.resolve(thenable);
p1.then(function (value) {
  console.log(value);  // 42
});

.Promise.reject()

`Promise.reject(reason)`方法也会返回一个新的 Promise 实例,该实例的状态为`rejected`。

const p = Promise.reject('出错了');
// 等同于
const p = new Promise((resolve, reject) => reject('出错了'))

p.then(null, function (res) {
  console.log(res)
});
// 出错了

Promise.reject()``方法的参数,会原封不动地作为reject的理由,变成后续方法的参数。

Promise.reject('出错了')
.catch(e => {
  console.log(e === '出错了') // true
})

.应用 promise来加载图片

const preloadImage = function (path) {
  return new Promise(function (resolve, reject) {
    const image = new Image();
    image.onload  = resolve;
    image.onerror = reject;
    image.src = path;
  });
};

参考:阮一峰es6zhuanlan.zhihu.com/p/325870525 , zhuanlan.zhihu.com/p/532452298