1、Promise——解决回调地狱
-
Promise是一个构造函数
const p1 = new Promise() //表示一个异步的操作
-
Promiseb本身上有all、reject、resolve等方法,Promise.protype原型上包含.then 、.catch等方法用于处理异步状态成功失败的回调
Promise.protype.then() ——> p1.__proyo__.then() //原型链
-
三种状态
> //一般将Promise放在一个函数当中
> function runAsync(){
> //Promise的构造函数需要我们传入一个函数作为参数,这个函数两个形参resolve和reject来决定状态
> var p = new Promise(function(resolve, reject){
> //异步操作
> $.ajax({
> url: 'xxx.com',
> data: {name:'krry'},
> success: res => {
> resolve(res); // 成功
> },
> error: err => {
> reject(err); // 失败
> }
> })
> });
> return p;
> }
-
异步操作"未完成"(pending)
- 异步操作"已完成" (resolved),对应状态为fullfiled
- 异步操作"失败" (rejected),对应状态为rejected
这三种状态的变化途径只有2种:
1,异步操作从“未完成”pending到”已完成“resolved
2,异步操作从“未完成"pending到”失败“rejected
状态一旦改变,就无法再次改变状态,这也是它名字 promise-承诺 的由来
所以Promise对象的最终结果只有两种:
1,异步操作成功 Promise 对象传回一个值,状态变为 resolved
2,异步操作失败 Promise 对象抛出一个错误,状态变为 rejected
-
.then()
1、then接收两个参数,并且这两个参数都是带有各自参数的函数,前者为成功时的函数,后者为失败时的函数(第二个参数是可选参数)
2、then函数返回一个Promise
若传入的函数并没有返回值,则将undefined作为下一个Promise的data;
若传入的函数返回值是一个普通值(非Promise),则将这个值作为下一个Promise的data;
若传入的函数返回值是一个Promise,则等待这个Promise的决议完成,再将这个结果作为下一个Promise的返回值。
//then 方法有两个参数,第一个是成功 resolve 的回调,第二个是失败 reject 的回调 runAsync().then(data => { console.log(data); // 请求到的数据 console.log('请求数据成功!'); }, err => { // 这是 then 的第二个参数,请求失败 reject 的回调 console.log('请求失败!'); });
-
.catch()
相当于 then 的第二个参数,失败 reject 的回调
runAsync().then(data => { console.log(data); // 请求到的数据 console.log('请求数据成功'); }).catch(err => { // 这是 catch,请求失败 reject 的回调 console.log('请求失败'); });
-
解决回调地狱
做异步请求的时候,在请求成功的回调函数里继续写函数,或者继续发送异步,继续回调,回调函数里又回调,一层一层,嵌套越来越多,就会形成回调地狱。这会使我们的代码可读性变差,不好维护,性能也下降。
Promise 就是用同步的方式写异步的代码,用来解决回调问题
-
Promise.all()和Promise.race()
1、Promise.all(),将多个Promise实例包装成一个新的Promise实例(等待机制)
//成功和失败的返回值是不同的,成功的时候返回的是一个结果数组,而失败的时候则返回最先被reject失败状态的值。 //所有的异步操作结束之后才会执行下一步的then(),可以保障promise请求的顺序 let wake = (time) => { return new Promise((resolve, reject) => { setTimeout(() => { resolve(`休眠${time}ms`) }, time) }) } let p1 = wake(3000) let p2 = wake(2000) Promise.all([p1, p2]).then((result) => { console.log(result) // [ '休眠3s', '休眠2s' ] }).catch((error) => { console.log(error) }) 注意的是,Promise.all获得的成功结果的数组里面的数据的顺序,和Promise.all接收到的数组顺序是会一致的,即p1的结果在前,即便p1的结果获取的比p2要晚,。这带来了一个绝大的好处:在前端开发请求数据的过程中,偶尔会遇到发送多个请求并根据请求顺序获取和使用数据的场景,使用Promise.all毫无疑问可以解决这个问题。 ``
2、Promise.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' })
2、async … await ——简化Promise的操作(同步写法)
注意点:
await只能放在async函数里
在async…await中,第一个await之前的代码是同步执行,之后的代码是异步执行
console.log('1') async function getData(){ console.log('2') let res1 = await axios.get('/user') console.log('3') } console.log('4') 结果:1 2 4 3
async函数,async 函数(有函数语句、函数表达式、Lambda表达式)会返回一个 Promise 对象
async function testAsync() { return "hello async"; //返回一个直接量 } //如果在函数中 `return` 一个直接量,async 会把这个直接量通过 `Promise.resolve()` 封装成 Promise 对象 const result = testAsync(); console.log(result); //输出Promise { 'hello async' }一个promise对象
**注:**Promise.resolve(x) 可以看作是 new Promise(resolve => resolve(x)) 的简写
返回一个promise对象,就可以then()来处理
testAsync().then(v => { console.log(v); // 输出 hello async });
如果 async 函数没有返回值,它会返回
Promise.resolve(undefined)
。Promise 的特点是异步的,无需等待,所以在没有
await
的情况下执行 async 函数,它会立即执行,返回一个 Promise 对象,并且,绝不会阻塞后面的语句。这和普通返回 Promise 对象的函数并无二致。
一般来说,都认为 await 是在等待一个 async 函数完成。不过按语法说明,await 等待的是一个表达式,这个表达式的计算结果是 Promise 对象或者其它值。
await要配合await来使用,await意思是等待,从他的语法书写可以看出, 它是在等待后面代码执行完成,即等待一个 async 函数的返回值
上面说了async是可以返回一个直接量,或者任意的返回值,所以await 后面实际是可以接普通函数调用或者直接量的
function getSomething() { return "something"; } async function testAsync() { return Promise.resolve("hello async"); } async function test() { const v1 = await getSomething(); const v2 = await testAsync(); console.log(v1, v2); } test();
await
可以看做是个一个处理异步运算符,用于组成表达式,await 表达式的运算结果取决于它等的东西。如果它等到的不是一个 Promise 对象,那 await 表达式的运算结果就是它等到的东西。
await
等待的不是一个 Promise Like 对象的时候,相当于await Promise.resolve(...)
。如果它等到的是一个 Promise 对象,await 就忙起来了,它会阻塞后面的代码,等着 Promise 对象 resolve,然后得到 resolve 的值,作为 await 表达式的运算结果。
async 会将其后的函数(函数表达式或 Lambda)的返回值封装成一个 Promise 对象,而 await 会等待这个 Promise 完成,并将其 resolve 的结果返回出来。
举例,用
setTimeout
模拟耗时的异步操作,先来看看不用 async/await 会怎么写function takeLongTime() { return new Promise(resolve => { setTimeout(() => resolve("long_time_value"), 1000); }); } takeLongTime().then(v => { console.log("got", v); });
如果改用 async/await 呢,会是这样
function takeLongTime() { return new Promise(resolve => { setTimeout(() => resolve("long_time_value"), 1000); }); } //takeLongTime() 没有申明为 async。实际上,takeLongTime() 本身就是返回的 Promise 对象,加不加 async 结果都一样 async function test() { const v = await takeLongTime(); console.log(v); } test();
如果一个函数本身就返回 Promise 对象,加
async
和不加async
还是有一点点区别:加了async
之后外面得到 Promise 对象并不是return
的那一个,参阅代码:(() => { let promise; async function test() { promise = new Promise(resolve => resolve(0)); promise.mark = "hello"; return promise; } const gotPromise = test(); console.log(`is same object?: ${promise === gotPromise}`); // false console.log(`promise.mark: ${promise.mark}`); // hello console.log(`gotPromise.mark: ${gotPromise.mark}`); // undefined })();
了解这一点后,如果我们需要在返回的 Promise 对象上附加一些东西,比如
cancel()
,就得小心一点。