前言
最近在开发中遇到一个问题:Table行内有下拉组件四级联动,而且还可以添加新行,每个新行又是四级联动,问:如何解决?想了半天于是使用Promise解决,让我重新对Promise有了认识。
Promise详解
通过MDN 官方文档对Promise有段介绍:Promise 对象用于表示一个异步操作的最终完成 (或失败)及其结果值。
一个 Promise 对象代表一个在这个 promise 被创建出来时不一定已知的值。它让您能够把异步操作最终的成功返回值或者失败原因和相应的处理程序关联起来。 这样使得异步方法可以像同步方法那样返回值:异步方法并不会立即返回最终的值,而是会返回一个 promise,以便在未来某个时候把值交给使用者。
一个 Promise 必然处于以下几种状态之一:
- 待定(pending): 初始状态,既没有被兑现,也没有被拒绝。
- 已兑现(fulfilled): 意味着操作成功完成。
- 已拒绝(rejected): 意味着操作失败。
待定状态的 Promise 对象要么会通过一个值被兑现(fulfilled),要么会通过一个原因(错误)被拒绝(rejected)。当这些情况之一发生时,我们用 promise 的 then 方法排列起来的相关处理程序就会被调用。如果 promise 在一个相应的处理程序被绑定时就已经被兑现或被拒绝了,那么这个处理程序就会被调用,因此在完成异步操作和绑定处理方法之间不会存在竞争状态。
因为 Promise.prototype.then 和 Promise.prototype.catch 方法返回的是 promise, 所以它们可以被链式调用。
1、构造函数
Promise()
创建一个新的 Promise 对象。该构造函数主要用于包装还没有添加 promise 支持的函数。
2、静态方法
Promise.all(iterable)
这个方法返回一个新的promise对象,该promise对象在iterable参数对象里所有的promise对象都成功的时候才会触发成功,一旦有任何一个iterable里面的promise对象失败则立即触发该promise对象的失败。这个新的promise对象在触发成功状态以后,会把一个包含iterable里所有promise返回值的数组作为成功回调的返回值,顺序跟iterable的顺序保持一致;如果这个新的promise对象触发了失败状态,它会把iterable里第一个触发失败的promise对象的错误信息作为它的失败错误信息。Promise.all方法常被用于处理多个promise对象的状态集合。(可以参考jQuery.when方法---译者注)
Promise.allSettled(iterable)
等到所有promises都已敲定(settled)(每个promise都已兑现(fulfilled)或已拒绝(rejected))。
返回一个promise,该promise在所有promise完成后完成。并带有一个对象数组,每个对象对应每个promise的结果。
Promise.any(iterable)
接收一个Promise对象的集合,当其中的一个 promise 成功,就返回那个成功的promise的值。
Promise.race(iterable)
当iterable参数里的任意一个子promise被成功或失败后,父promise马上也会用子promise的成功返回值或失败详情作为参数调用父promise绑定的相应句柄,并返回该promise对象。
Promise.reject(reason)
返回一个状态为失败的Promise对象,并将给定的失败信息传递给对应的处理方法
Promise.resolve(value)
返回一个状态由给定value决定的Promise对象。如果该值是thenable(即,带有then方法的对象),返回的Promise对象的最终状态由then方法执行决定;否则的话(该value为空,基本类型或者不带then方法的对象),返回的Promise对象状态为fulfilled,并且将该value传递给对应的then方法。通常而言,如果您不知道一个值是否是Promise对象,使用Promise.resolve(value) 来返回一个Promise对象,这样就能将该value以Promise对象形式使用。
使用Promise
通过上面可以详细了解Promise的静态方法,后来就是使用Promise实例,
创建Promise
Promise 对象是由关键字 new 及其构造函数来创建的。该构造函数会把一个叫做“处理器函数”(executor function)的函数作为它的参数。这个“处理器函数”接受两个函数——resolve 和 reject ——作为其参数。当异步任务顺利完成且返回结果值时,会调用 resolve 函数;而当异步任务失败且返回失败原因(通常是一个错误对象)时,会调用reject 函数。
const myFirstPromise = new Promise((resolve, reject) => {
// ?做一些异步操作,最终会调用下面两者之一:
//
// resolve(someValue); // fulfilled
// ?或
// reject("failure reason"); // rejected
});
想要某个函数拥有promise功能,只需让其返回一个promise即可。
function myAsyncFunction(url) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open("GET", url);
xhr.onload = () => resolve(xhr.responseText);
xhr.onerror = () => reject(xhr.statusText);
xhr.send();
});
};
基础示例
let myFirstPromise = new Promise(function(resolve, reject){
//当异步代码执行成功时,我们才会调用resolve(...), 当异步代码失败时就会调用reject(...)
//在本例中,我们使用setTimeout(...)来模拟异步代码,实际编码时可能是XHR请求或是HTML5的一些API方法.
setTimeout(function(){
resolve("成功!"); //代码正常执行!
}, 250);
});
myFirstPromise.then(function(successMessage){
//successMessage的值是上面调用resolve(...)方法传入的值.
//successMessage参数不一定非要是字符串类型,这里只是举个例子
console.log("Yay! " + successMessage);
});
Promise.all的基础使用
Promise.all() 方法接收一个promise的iterable类型(注:Array,Map,Set都属于ES6的iterable类型)的输入,并且只返回一个Promise实例, 那个输入的所有promise的resolve回调的结果是一个数组。这个Promise的resolve回调执行是在所有输入的promise的resolve回调都结束,或者输入的iterable里没有promise了的时候。它的reject回调执行是,只要任何一个输入的promise的reject回调执行或者输入不合法的promise就会立即抛出错误,并且reject的是第一个抛出的错误信息。
const promise1 = Promise.resolve(3);
const promise2 = 42;
const promise3 = new Promise((resolve, reject) => {
setTimeout(resolve, 100, 'foo');
});
Promise.all([promise1, promise2, promise3]).then((values) => {
console.log(values);
});
// expected output: Array [3, 42, "foo"]
输出:
> Array [3, 42, "foo"]
Promise日常使用
日常开发情况下我们会经常跟Promise打交道,下面列举日常使用方式,
先定义一个Promise方法,
api.js
function getData(){
return new Promise(resolve => {
axios.get('xxxx').then(res => {
resolve(res.data)
})
}
}
然后在组件里面调用分成两种方式接收:1、async await,2、then,看个人喜好使用哪种方式。
async await方式
async function query(){
// res接收
let res = await api.getData();
}
then方式
function query(){
api.getData().then(res=>{
// res接收
});
}
Promise.all复杂使用
下面举个四级联动的例子介绍Promise.all的用法,可以跟Array.map方法结合使用,如下所示,数组中包含选中值:one,two,three和four,以及选中值对应的列表:oneList,twoList,threeList和fourList,并且四组数据之间存在关联关系。
let arry = [{
one: '',
oneList: [],
two: '',
twoList: [],
three: '',
threeList: [],
four: '',
fourList: []
},{
one: '',
oneList: [],
two: '',
twoList: [],
three: '',
threeList: [],
four: '',
fourList: []
},{
one: '',
oneList: [],
two: '',
twoList: [],
three: '',
threeList: [],
four: '',
fourList: []
}]
// 获取所有oneList数据
Promise.all(arr.map(item => this.getOneList()))
.then(promiseData => {
//按顺序输出id对应的oneList 数据
arr.forEach((item,index) => {
item.oneList = promiseData[index]
})
})
// 获取所有twoList数据
Promise.all(arr.map(item => this.getTwoList(item.one)))
.then(promiseData => {
//按顺序输出id对应的twoList 数据
arr.forEach((item,index) => {
item.twoList = promiseData[index]
})
})
// 获取所有threeList数据
Promise.all(arr.map(item => this.getThreeList(item.two)))
.then(promiseData => {
//按顺序输出id对应的threeList 数据
arr.forEach((item,index) => {
item.threeList = promiseData[index]
})
})
// 获取所有fourList数据
Promise.all(arr.map(item => this.getFourList(item.three)))
.then(promiseData => {
//按顺序输出id对应的fourList 数据
arr.forEach((item,index) => {
item.fourList = promiseData[index]
})
})
这样就能批量获取四级列表数据并赋值到arry数组上面,一开始我的解决思路并不是这样,而是在forEach里面使用Promise,发现根本行不通,因为forEach本身就是异步的,所以还没等结果返回就已经执行了。
错误使用方式
arry.forEach((item, index) => {
item.oneList = this.getOneList();
item.twoList = this.getTwoList(item.one);
item.threeList = this.getThreeList(item.one);
item.fourList = this.getFourList(item.one);
})
虽然很简单,但是实际上行不通,一定要注意。
总结:
1、forEach不支持Promise
引用
JavaScript ES6 promise for loop duplicate
How to return many Promises and wait for them all before doing other stuff
Promise.all结合async/await
理解和使用Promise.all和Promise.race
JAVASCRIPT中FOREACH的异步问题
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。