promise的作用与使用

promise的理解与使用

promise是什么

1. 理解promise
抽象表达:Promise是JS中进行异步编程的新的解决方案(旧方案是单纯使用回调函数)
---- 异步编程 ①fs 文件操作 ②数据库操作 ③Ajax ④定时器
具体表达:
①从语法上看:Promise是一个构造函数 (自己身上有all、reject、resolve这几个方法,原型上有then、catch等方法)
②从功能上看:promise对象用来封装一个异步操作并可以获取其成功/失败的结果值
阮一峰的解释:
所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果
从语法上说,Promise 是一个对象,从它可以获取异步操作的消息
Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理
2. Promise 的状态
实例对象promise中的一个属性 PromiseState
1.pending 变为 resolved/fullfilled
2.pending 变为 rejected
注意:
对象的状态不受外界影响
只有这两种,且一个 promise 对象只能改变一次
一旦状态改变,就不会再变,任何时候都可以得到这个结果
无论成功还是失败,都会有一个结果数据。成功的结果数据一般称为 value,而失败的一般称为 reason。
3. Promise对象的值
实例对象promise的另一个值 PromiseResult
保存着对象 成功/失败 的值(value/reason)
resolve/reject可以修改值
4. Promise 的基本流程
在这里插入图片描述
5. Promise 的基本使用

const promise = new Promise(function(resolve, reject) {
// ... some code
if (/* 异步操作成功 */){
resolve(value);
} else {
reject(reason);
}
});

Promise构造函数接受一个函数(执行器函数)作为参数,该函数的两个参数分别是resolve和reject。它们是两个函数,由 JavaScript 引擎提供,不用自己部署。
resolve函数的作用是,将Promise对象的状态从“未完成”变为“成功”(即从 pending 变为 resolved),在异步操作成功时调用,并将异步操作的结果,作为参数value传递出去;
reject函数的作用是,将Promise对象的状态从“未完成”变为“失败”(即从 pending 变为 rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数error/reason传递出去。
Promise实例生成以后,可以用then方法分别指定resolved状态和rejected状态的回调函数。

promise.then(function(value) {
  // success
}, function(reason) {
  // failure
});

then方法可以接受两个回调函数作为参数。
第一个回调函数onResolved()是Promise对象的状态变为resolved时调用
第二个回调函数onRejected()是Promise对象的状态变为rejected时调用
这两个函数都是可选的,不一定要提供。它们都接受Promise对象传出的值作为参数
案例:

function rand(m, n) {
            return Math.ceil(Math.random() * (n - m + 1)) + (m - 1)
  }

const p = new Promise((resolve, reject) => {
                setTimeout(() => {
                    const a = rand(1, 100)
                    if (a <= 30) {
                        resolve(a)
                    } else {
                        reject(a)
                    }
                }, 1000)
            })
            console.log(p);
            p.then((value) => {
                alert("恭喜你中奖了,你的号码是"+value)
            }, (reson) => {
                alert("再接再厉,你的号码是:"+reson)
            })

        })
.then() 和执行器(executor)同步执行,.then() 中的回调函数异步执行

为什么要用 Promise

1. 指定回调函数的方式更加灵活
旧的:必须在启动异步任务前指定

// 1. 纯回调的形式
// 成功的回调函数
function successCallback(result) {
  console.log("声音文件创建成功:" + result);
}
// 失败的回调函数
function failureCallback(error) {
  console.log("声音文件创建失败:" + error);
}
// 必须先指定回调函数,再执行异步任务
createAudioFileAsync(audioSettings, successCallback, failureCallback) // 回调函数在执行异步任务(函数)前就要指定

promise:启动异步任务 => 返回promise对象 => 给promise对象绑定回调函数(甚至可以在异步任务结束后指定)

// 2. 使用Promise
const promise = createAudioFileAsync(audioSettings);  // 执行2秒
setTimeout(() => {
  promise.then(successCallback, failureCallback) // 也可以获取
}, 3000);

2. 支持链式调用,可以解决回调地狱问题
什么是回调地狱?

回调函数嵌套调用,外部回调函数异步执行的结果是其内部嵌套的回调函数执行的条件

doSomething(function(result) {
  doSomethingElse(result, function(newResult) {
    doThirdThing(newResult, function(finalResult) {
      console.log('Got the final result:' + finalResult)
    }, failureCallback)
  }, failureCallback)
}, failureCallback)

回调地狱的缺点?

  1. 不便于阅读
  2. 不便于异常处理

解决方案?

  • promise 链式调用

使用 promise 的链式调用解决回调地狱

doSomething()
  .then(result => doSomethingElse(result))
  .then(newResult => doThirdThing(newResult))
  .then(finalResult => {console.log('Got the final result:' + finalResult)})
  .catch(failureCallback)

终极解决方案?

  • async/await

回调地狱的终极解决方案 async/await

async function request() {
  try{
    const result = await doSomething()
    const newResult = await doSomethingElse(result)
    const finalResult = await doThirdThing(newResult)
    console.log('Got the final result:' + finalResult)
  } catch (error) {
    failureCallback(error)
  }
}

如何使用 Promise

Promise 构造函数:Promise(executor) {}

executor 函数:同步执行 (resolve, reject) => {}

resolve 函数:内部定义成功时调用的函数 resove(value)

reject 函数:内部定义失败时调用的函数 reject(reason)

说明:executor 是执行器,会在 Promise 内部立即同步回调,异步操作 resolve/reject 就在 executor 中执行

Promise.prototype.then 方法:p.then(onResolved, onRejected)

指定两个回调(成功+失败)

onResolved 函数:成功的回调函数 (value) => {}

onRejected 函数:失败的回调函数 (reason) => {}

说明:指定用于得到成功 value 的成功回调和用于得到失败 reason 的失败回调,返回一个新的 promise 对象

Promise.prototype.catch 方法:p.catch(onRejected)

指定失败的回调

1)onRejected 函数:失败的回调函数 (reason) => {}

说明:这是then() 的语法糖,相当于 then(undefined, onRejected)

new Promise((resolve, reject) => { // excutor执行器函数
 setTimeout(() => {
   if(...) {
     resolve('成功的数据') // resolve()函数
   } else { 
     reject('失败的数据') //reject()函数
    }
 }, 1000)
}).then(
 value => { // onResolved()函数
  console.log(value) // 成功的数据
}
).catch(
 reason => { // onRejected()函数
  console.log(reason) // 失败的数据
}
)

Promise.resolve 方法:Promise.resolve(value)

value:将被 Promise 对象解析的参数,也可以是一个成功或失败的 Promise 对象
返回:返回一个带着给定值解析过的 Promise 对象,如果参数本身就是一个 Promise 对象,则直接返回这个 Promise 对象。
如果传入的参数为 非Promise类型的对象, 则返回的结果为成功promise对象

let p1 = Promise.resolve(521);
console.log(p1); // Promise {<fulfilled>: 521}

在这里插入图片描述
如果传入的参数为 Promise 对象, 则参数的结果决定了 resolve 的结果

let p2 = Promise.resolve(new Promise((resolve, reject) => {
    // resolve('OK'); // 成功的Promise
    reject('Error');
}));
console.log(p2);
p2.catch(reason => {
    console.log(reason);
})

在这里插入图片描述

Promise.reject 方法:Promise.resolve(reason)

reason:失败的原因
说明:返回一个失败的 promise 对象

let p = Promise.reject(521);
let p2 = Promise.reject('iloveyou');
let p3 = Promise.reject(new Promise((resolve, reject) => {
    resolve('OK');
}));

console.log(p);
console.log(p2);
console.log(p3);

在这里插入图片描述

  • Promise.resolve()/Promise.reject() 方法就是一个语法糖
  • 用来快速得到Promise对象
//产生一个成功值为1的promise对象
new Promise((resolve, reject) => {
 resolve(1)
})
//相当于
const p1 = Promise.resolve(1)
const p2 = Promise.resolve(2)
const p3 = Promise.reject(3)

p1.then(value => {console.log(value)}) // 1
p2.then(value => {console.log(value)}) // 2
p3.catch(reason => {console.log(reason)}) // 3

Promise.all 方法:Promise.all(iterable)

iterable:包含 n 个 promise 的可迭代对象,如 ArrayString
说明:返回一个新的 promise,只有所有的 promise 都成功才成功,只要有一个失败了就直接失败

<script>
        //promise.all必须全部promise对象返回的结果是真,结果才会为真

        const p1 = new Promise((resolve, reject) => {
            // reject("erro")
            setTimeout(() => {
                resolve("success")
            }, 1000)
        })

        const p2 = new Promise((resolve, reject) => {
            resolve("ok")
            // reject("erro")
        })

        const p3 = new Promise((resolve, reject) => {
            resolve("ok")
            // reject("erro")
        })


        const p4 = Promise.all([p1, p2, p3])
        console.log(p4);
    </script>

在这里插入图片描述

Promise.race方法:Promise.race(iterable)

iterable:包含 n 个 promise 的可迭代对象,如 ArrayString

说明:返回一个新的 promise,第一个完成的 promise 的结果状态就是最终的结果状态谁先完成就输出谁(不管是成功还是失败)

const pRace = Promise.race([p1, p2, p3])
// 谁先完成就输出谁(不管是成功还是失败)
const p1 = new Promise((resolve, reject) => {
 setTimeout(() => {
   resolve(1)
 }, 1000)
})
const p2 = Promise.resolve(2)
const p3 = Promise.reject(3)

pRace.then(
value => {
   console.log('race onResolved()', value)
 },
reason => {
   console.log('race onRejected()', reason) 
 }
)
//race onResolved() 2
let p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
      resolve('OK');
  }, 1000);
})
let p2 = Promise.resolve('Success');
let p3 = Promise.resolve('Oh Yeah');

//调用
const result = Promise.race([p1, p2, p3]);

console.log(result);

在这里插入图片描述

Promise 的几个关键问题

如何改变 promise 的状态?

(1)resolve(value):如果当前是 pending 就会变为 resolved

(2)reject(reason):如果当前是 pending 就会变为 rejected

(3)抛出异常:如果当前是 pending 就会变为 rejected

const p = new Promise((resolve, reject) => {
  //resolve(1) // promise变为resolved成功状态
  //reject(2) // promise变为rejected失败状态
  throw new Error('出错了') // 抛出异常,promise变为rejected失败状态,reason为抛出的error
})
p.then(
  value => {},
  reason => {console.log('reason',reason)}
)
// reason Error:出错了

一个 promise 指定多个成功/失败回调函数,都会调用吗?

当 promise改变为对应状态时都会调用

const p = new Promise((resolve, reject) => {
  //resolve(1)
  reject(2)
})
p.then(
  value => {},
  reason => {console.log('reason',reason)}
)
p.then(
  value => {},
  reason => {console.log('reason2',reason)}
)
// reason 2
// reason2 2

改变 promise 状态和指定回调函数谁先谁后?

都有可能,常规是先指定回调再改变状态,但也可以先改状态再指定回调

  • 如何先改状态再指定回调?

(1)在执行器中直接调用 resolve()/reject()

(2)延迟更长时间才调用 then()

let p = new Promise((resolve, reject) => {
  resolve('OK');//这个时候会先改变状态在指定回调
	setTimeout(()=>{
				resolve("ok") //这个时候先指定回调在改变状态
		})
});

p.then(value => {
  console.log(value);
},reason=>{
  
})
  • 什么时候才能得到数据?

(1)如果先指定的回调,那当状态发生改变时,回调函数就会调用得到数据

(2)如果先改变的状态,那当指定回调时,回调函数就会调用得到数据

new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve(1) // 改变状态
  }, 1000)
}).then( // 指定回调函数 (先指定)
  value => {},
  reason =>{}
)

此时,先指定回调函数,保存当前指定的回调函数;后改变状态(同时指定数据),然后异步执行之前保存的回调函数。

new Promise((resolve, reject) => {
  resolve(1) // 改变状态
}).then( // 指定回调函数
  value => {},
  reason =>{}
)

这种写法,先改变的状态(同时指定数据),后指定回调函数(不需要再保存),直接异步执行回调函数

promise.then() 返回的新 promise 的结果状态由什么决定?

(1)简单表达:由 then()指定的回调函数执行的结果决定

let p = new Promise((resolve, reject) => {
  resolve('ok');
});
//执行 then 方法
let result = p.then(value => {
  console.log(value);
}, reason => {
  console.warn(reason);
});

console.log(result);

在这里插入图片描述
因为返回的是undefined
(2)详细表达:
① 如果抛出异常,新 promise 变为 rejectedreason 为抛出的异常

let p = new Promise((resolve, reject) => {
  resolve('ok');
});
//执行 then 方法
let result = p.then(value => {
  //1. 抛出错误
  throw '出了问题';
}, reason => {
  console.warn(reason);
});

console.log(result);

在这里插入图片描述
② 如果返回的是非 promise 的任意值,新 promise变为 resolved,value为返回的值

let p = new Promise((resolve, reject) => {
  resolve('ok');
});
//执行 then 方法
let result = p.then(value => {
	//2. 返回结果是非 Promise 类型的对象
	return 521;
}, reason => {
  console.warn(reason);
});

console.log(result);

在这里插入图片描述
③ 如果返回的是另一个新 promise,此 promise的结果就会成为新 promise的结果

let p = new Promise((resolve, reject) => {
  resolve('ok');
});
//执行 then 方法
let result = p.then(value => {
	//3. 返回结果是 Promise 对象
	return new Promise((resolve, reject) => {
		// resolve('success');
		reject('error');
	});
}, reason => {
  console.warn(reason);
});

console.log(result);

在这里插入图片描述

promise 如何串联多个操作任务?

(1)promisethen() 返回一个新的 promise,可以并成 then() 的链式调用
(2)通过 then 的链式调用串联多个同步/异步任务

let p = new Promise((resolve, reject) => {
  setTimeout(() => {
      resolve('OK');
  }, 1000);
});

p.then(value => {
  return new Promise((resolve, reject) => {
      resolve("success");
  });
}).then(value => {
  console.log(value); // success
}).then(value => {
  console.log(value); // undefined
})

Promise 异常穿透(传透)?

(1)当使用 promisethen 链式调用时,可以在最后指定失败的回调
(2)前面任何操作出了异常,都会传到最后失败的回调中处理

new Promise((resolve, reject) => {
   //resolve(1)
   reject(1)
}).then(
  value => {
    console.log('onResolved1()', value)
    return 2
  }
).then(
  value => {
    console.log('onResolved2()', value)
    return 3
  }
).then(
  value => {
    console.log('onResolved3()', value)
  }
).catch(
  reason => {
    console.log('onRejected1()', reason)
  }
)
// onRejected1() 1

相当于这种写法:多写了很多reason => {throw reason}

new Promise((resolve, reject) => {
   //resolve(1)
   reject(1)
}).then(
  value => {
    console.log('onResolved1()', value)
    return 2
  },
  reason => {throw reason} // 抛出失败的结果reason
).then(
  value => {
    console.log('onResolved2()', value)
    return 3
  },
  reason => {throw reason} // 抛出失败的结果reason
).then(
  value => {
    console.log('onResolved3()', value)
  },
  reason => {throw reason} // 抛出失败的结果reason
).catch(
  reason => {
    console.log('onRejected1()', reason)
  }
)
// onRejected1() 1

所以失败的结果是一层一层处理下来的,最后传递到 catch 中。
或者,将 reason => {throw reason} 替换为 reason => Promise.reject(reason) 也是一样的。

中断 promise 链?

当使用 promisethen 链式调用时,在中间中断,不再调用后面的回调函数
办法:在回调函数中返回一个 pending 状态的 promise 对象

new Promise((resolve, reject) => {
   //resolve(1)
   reject(1)
}).then(
  value => {
    console.log('onResolved1()', value)
    return 2
  }
).then(
  value => {
    console.log('onResolved2()', value)
    return 3
  }
).then(
  value => {
    console.log('onResolved3()', value)
  }
).catch(
  reason => {
    console.log('onRejected1()', reason)
  }
).then(
  value => {
    console.log('onResolved4()', value)
  },
  reason => {
    console.log('onRejected2()', reason)
  }
)
// onRejected1() 1
// onResolved4() undefined

catch 中返回一个新的 promise,且这个 promise 没有结果。
由于,返回的新的 promise 结果决定了后面 then 中的结果,所以后面的 then 中也没有结果。
这就实现了中断 promise链的效果。

async await

aysnc函数

1.函数的返回值为promise对象
2.promise对象得结果由async函数执行得返回值决定

<script>
        async function main(){

        }

        const m = main()
        console.log(m);
    </script>

在这里插入图片描述

<script>
        async function main() {
            //如果返回的是一个非promise对象 返回结果为成功得promise
            // return 123
            //如果返回的是promise对象 返回结果由promise对象得返回结果决定
            return new Promise((resolve, reject) => {
                reject("no")
            })
        }

        const m = main()
        console.log(m);
    </script>

在这里插入图片描述

await函数

1.await右侧得表达式一般为promise对象,但也可以是其他得值

2.如果表达式是promise对象,await返回的是promise成功得值

3.如果表达式是其他值,直接将此作为await得返回值

4.await必须搭配async使用

5.如果await得promise失败,就会抛出异常,需要用try catch 捕获异常

<script>
        async function main() {
            const p = new Promise((resolve, reject) => {
                // resolve("ok")
                reject("error")
            })
            //为成功的promise对象
            // let a = await p
            // console.log(a);
            // //为其他类型
            // let b = await 123
            // console.log(b);

            try {
                let c = await p
            } catch (e) {
                console.log(e);
            }


        }

        const m = main()
        // console.log(m);
    </script>

在这里插入图片描述

async await 结合发送Ajax请求

<button id="btn">发送Ajax请求</button>
    <script>
        function sendAjax(url) {
            return new Promise((resolve, reject) => {
                const xhr = new XMLHttpRequest()
                xhr.open('Get', url)
                xhr.send()
                xhr.onreadystatechange = function () {
                    if (xhr.readyState === 4) {
                        if (xhr.status >= 200 && xhr.status < 300) {
                            resolve(xhr.response)
                        } else {
                            reject(xhr.status)
                        }
                    }
                }
            }).then(value => console.log(value), reson => console.log(reson))
        }
        const btn = document.querySelector("#btn")

        btn.addEventListener("click", async function () {
            let data = await sendAjax("https??api.apiopen.top/getJoke")
            console.log(data);
        })
    </script>

js异步之宏队列与微队列

原理图:
在这里插入图片描述

    <script>
        setTimeout(()=>{
            console.log(1);
            Promise.resolve(5).then(v=>{
            console.log(v);
        })
        },0)
        setTimeout(()=>{
            console.log(4);
        },0)
        Promise.resolve(2).then(v=>{
            console.log(v);
        })
        Promise.resolve(3).then(v=>{
            console.log(v);
        })
    </script>

在这里插入图片描述
1.js中用来存储待执行回调函数的队列包含2个不同的特定的队列

2.宏队列:用来保存待执行的宏任务(回调),比如:定时器 / DOM事件回调/ ajax回调

3.微队列:用来保存待执行的微任务(回调),比如:promise的回调/MutationObserver的回调

4.js执行时会区别这2个队列

面试题:

1.<script>
        setTimeout(() => {
            console.log(1);
        })
        Promise.resolve().then(() => {
            console.log(2);
        })
        Promise.resolve().then(() => {
            console.log(4);
        })
        console.log(3);
        //3 2 4 1
    </script>

在这里插入图片描述

2.<script>
        setTimeout(() => {
            console.log(1);
        })
        new Promise((resolve) => {
            console.log(2);
            resolve()
        }).then(() => {
            console.log(3);
        }).then(() => {
            console.log(4);
        })
        console.log(5);
        //2 5 3 4 1
    </script>

在这里插入图片描述

3.<script>
        const first = () => (
            new Promise((resolve, reject) => {
                console.log(3);
                let p = new Promise((resolve, reject) => {
                    console.log(7);
                    setTimeout(() => {
                        console.log(5);
                        resolve(6)
                    }, 0)
                    resolve(1)
                })
                resolve(2)
                p.then((arg) => {
                    console.log(arg);
                })
            })
        )
        first().then((arg) => {
            console.log(arg);
        })
        console.log(4);
        //3 7 4 1 2 5
    </script>

在这里插入图片描述

4.<script>
        setTimeout(() => {
            console.log(0);
        })
        new Promise((resolve, reject) => {
            console.log(1);
            resolve()
        }).then(() => {
            console.log(2);
            new Promise((resolve, reject) => {
                console.log(3);
                resolve()
            }).then(() => {
                console.log(4);
            }).then(() => {
                console.log(5);
            })
        }).then(() => {
            console.log(6);
        })
        new Promise((resolve, reject) => {
            console.log(7);
            resolve()
        }).then(() => {
            console.log(8);
        })

        //1 7 2 3 8 4 6 5 0
    </script>

在这里插入图片描述

  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值