目录
1、Promise介绍
Promise 是异步编程的一种解决方案,接下来我会用延迟函数来模拟网络请求(异步操作)。
Promise是个对象,我们用他的时候是要new的,在new这个Promise的时候是要求传入参数的:
new Promise(参数)
而这个参数本身又是一个函数。也就是说,这个Promise在new的时候就要求我们给它传入一个函数作为参数,也就是:
new Promise(() => {})
但是我们给这个Promise传入的这个函数本身它也包含两个参数,一个叫resolve,一个叫reject,也就是:
new Promise((resolve, reject) => {})
而这里的两个参数 resolve 和 reject 本身又是函数。
以前我们处理异步请求:
// 用setTimeout模拟网络请求
setTimeout(() => {
console.log('Hello World');
// 我们以前会在网络请求回来以后在这里进行一些操作,比如数据处理等等
// 假如在这里我们又要进行异步操作,那么:
setTimeout(() => {
...
// 假如在这里我们又要进行异步操作,那么就会出现回调地狱的情况
}, 1000)
}, 1000)
现在用 Promise进行封装:
new Promise((resolve, reject) => {
// 用setTimeout模拟网络请求
setTimeout(() => {
// 我们在这里调用resolve方法
resolve()
}, 1000)
})
我们上面调用了resolve方法,那么它会自动的调用new Promise().then() 方法,也就是:
new Promise((resolve, reject) => {
// 用setTimeout模拟网络请求
setTimeout(() => {
// 我们在这里调用resolve方法
resolve()
}, 1000)
}).then()
而这个then方法接受的参数又是一个函数,也就是:
new Promise((resolve, reject) => {
// 用setTimeout模拟网络请求
setTimeout(() => {
// 我们在这里调用resolve方法
resolve()
}, 1000)
}).then(() => {})
那么我们可以把数据处理等操作放到这个then的方法里面去做:
new Promise((resolve, reject) => {
// 用setTimeout模拟网络请求
// 第一次网络请求
setTimeout(() => {
// 我们在这里调用resolve方法
resolve()
}, 1000)
}).then(() => {
// 处理第一次网络请求的结果
console.log('Hello World');
// 第二次网络请求
setTimeout(() => {
...
}, 1000)
})
但是上面还是有嵌套异步请求(第一次网络请求的结果和第二次网络请求放在一块了),我们可以再进行这样写:
new Promise((resolve, reject) => {
// 用setTimeout模拟网络请求
// 第一次网络请求
setTimeout(() => {
resolve()
}, 1000)
}).then(() => {
// 处理第一次网络请求的结果
console.log('Hello World');
return new Primise((resolve, reject) => {
// 第二次网络请求
setTimeout(() => {
resolve()
}, 1000)
})
}).then(() => {
// 处理第二次网络请求的结果
})
如果还有网络请求嵌套的话,还可以继续往下写,在then方法里面再返回new Promise…,上面这种就是链式编程
1.1 什么情况下会用到Promise
一般情况下是有异步操作时,使用Promise对异步操作进行封装。
把网络请求塞到Promise里面,请求返回结果后调用resolve方法,然后在then方法里进行数据处理等操作。
new Promise(() => {})
new Promise的时候做了什么呢:
1、在构造函数里保存一些状态信息
2、执行传入的函数,也就是上面的箭头函数
3、在执行传入的回调函数时,会传入两个参数,分别是resolve和 reject,他们又是函数
new Promise((resolve, reject) => {
// 网络请求
setTimeout(() => {
// 假设网络请求后返回的数据是 'Hello world'
resolve('Hello world')
}, 1000)
}).then((data) => { // 这里的这个data就是上面resolve方法传过来参数
// 数据处理
console.log(data); // Hello World
})
Promise将网络请求的代码和最终处理数据的代码做了分离,这样看起来用Promise封装反而会让代码变得更多,但是当网络请求变的复杂时,当网络请求嵌套时,Promise的方式写出来的代码会更加的优雅和清晰。
2、resolve是成功时调用,reject是失败时调用
new Promise((resolve, reject) => {
// 网络请求
setTimeout(() => {
// 成功时调用
// 假设网络请求后返回的数据是 'Hello world'
// resolve('Hello world')
// 请求失败时调用
reject('error message')
}, 1000)
}).then((data) => { // 这里的这个data就是上面resolve方法传过来的参数
// 数据处理
console.log(data);
}).catch((error) => { // 这里的这个error就是上面reject方法传过来的参数
console.log(error); // error message
})
调用resolve后在then方法里接受参数进行处理;调用reject方法后在catch方法里接受参数进行处理
2.1 另一种方式(写法)
then 方法里面也可以接受两个参数,这两个参数都是函数。第一个函数是resolve时调用,第二个参数是reject时调用。
new Promise((resolve, reject) => {
// 网络请求
setTimeout(() => {
resolve('Hello world')
reject('error message')
}, 1000)
}).then((data) => {
console.log(data); // Hello world
}, (err) => {
console.log(err); // error message
})
注意: 我上面的只是解释,真正写的时候,要么resolve,要么reject,他俩是互斥的,只会执行一个。
3、Promise简写方式
resolve 和 reject 两个参数,但是reject是可选参数,我们可以不写
new Promise(resolve => {
setTimeout(() => {
resolve('aaa')
})
}).then((data) => {
console.log(data); // aaa
return new Promise(resolve => {
resolve(data + 111);
})
}).then(data => {
console.log(data); // aaa111
})
我们发现上面代码的8、9、10行 返回的这个Promise里面并没有异步请求,只是单纯的resolve出去了一个数据而已,那么我们就可以进行简写:
new Promise(resolve => {
setTimeout(() => {
resolve('aaa')
})
}).then((data) => {
console.log(data); // aaa
return Promise.resolve(data + 111); // 这是简写的样子
}).then(data => {
console.log(data); // aaa111
})
但是我觉得上面的代码还是不够简洁,那么我们再简写:
new Promise(resolve => {
setTimeout(() => {
resolve('aaa')
})
}).then((data) => {
console.log(data); // aaa
return data + 111; // 这是简写的样子
}).then(data => {
console.log(data); // aaa111
return data + 222;
}).then((data) => {
console.log(data); // aaa111222
})
上面代码的第8行,直接return出去了数据,因为Promise内部在这里会进行一个Promise包装的,内部调了resolve方法,不用我们来做。
上述代码都是假设请求成功,调用了resolve方法,那么如果请求失败了呢,我们看下:
new Promise(resolve => {
setTimeout(() => {
resolve('aaa')
})
}).then((data) => {
console.log(data); // aaa
return new Promise((resolve, reject) => {
reject('出错了');
})
}).then(data => {
console.log(data);
return new Promise((resolve, reject) => {
resolve(data + 222);
})
}).then((data) => {
console.log(data);
}).catch((err) => {
console.log(err); // 出错了
})
我们发现只打印了 aaa 和 ‘出错了’, 其他的并没有执行。
那么我们的reject 也有简写:
new Promise(resolve => {
setTimeout(() => {
resolve('aaa')
})
}).then((data) => {
console.log(data); // aaa
return Promise.reject('出错了'); // 简写
}).then(data => {
console.log(data);
return data + 222;
}).then(data => {
console.log(data);
}).catch(err => {
console.log(err); // 出错了
})
4、Promise.all()
等多个网络请求都完成后再对数据进行处理。
Promise.all方法接受一个参数,这个参数是数组类型的,数组的每一项都是一个promise对象
Promise.all([
new Promise((resolve, reject) => {
$ajax({
url: 'url1',
success(data) {
resolve(data)
}
})
}),
new Promise((resolve, reject) => {
$ajax({
url: 'url2',
success(data) {
resolve(data)
}
})
})
]).then(results => {
// 这个results是个数组,里面分别是第一个请求的返回结果,第二个请求的返回结果,第三个....
})
我们可以用延迟函数进行网络请求的模拟:
Promise.all([
new Promise((resolve, reject) => {
setTimeout(() => {
resolve(111)
}, 1000)
}),
new Promise((resolve, reject) => {
setTimeout(() => {
resolve(222)
}, 2000)
})
]).then(results => {
// 这个results是个数组,里面分别是第一个请求的返回结果,第二个请求的返回结果,第三个....
console.log(results); // [111, 222]
})