写法说明:
Promise表示类
promise表示实例对象
基本用法
const result = await promise //xxx值 = 一个异步任务
简易例子
Promise的封装
function 摇色子(){return new Promise((resolve,reject)=>{setTimeout(()=>{resolve(Math.floor(Math.random()*6)+1)},3000)})}
- 必须return 一个new Promise
- new Promise接受一个函数,这个函数就是你要做的事情
- 函数要包含 resolve 成功后调用,reject 失败后调用
使用Promise
摇色子().then(s1,f1).then(s2,f2)
用控制台试一下

摇色子() 执行这个函数,会返回一个Promise

使用成功回调 s1。 在两秒钟之后打印出成功
- 获取摇色子结果

使用resolve方法的参数获取
Promise回调的执行时机
改写一下上面的代码,不再使用计时器了
function 摇色子(){return new Promise((resolve,reject)=>{resolve(Math.floor(Math.random()*6)+1)})}
再执行 一次then看看
甚至比Promise出来的还快一点
那么在之后加一个console.log呢
先打印出end,再出结果
很明显的结果,因为Promise是一个异步任务,所以之后的代码执行完再打印出结果是很容易想到的
那么如果之后的代码里面有其他的异步任务呢
先出promise结果,再出timeout
这就涉及到了常说的微任务和宏任务了:
由于本来是没有Promise的,ES6之后才有的。所以之前的js很单纯,就只有两个东西:当前任务,延时任务。
首先主线程会去执行所有的同步任务。等到同步任务完成之后,就会去看任务队列里面的异步任务。如果异步任务满足条件,那么异步任务就会重新进入主线程开始执行,这时候他就会变成同步任务。 就这么循环重复。
那ES6之后呢:
为了让Promise回调更早地执行,强行插入了一个队列
就把上面的异步队列,分成了两个:一个是微任务,一个是宏任务。就是两个队列
setimeout这些呢 放入宏任务的队列
ajax.then(),promise这些 放入微任务的队列
一般就先执行微任务里面的任务,执行完之后再去找宏任务里面的。
如果宏任务里面有微任务,就里面的微任务放进队列里,做完宏任务再去微任务,一直重复
这个就很清楚了。
先做完摇色子1(微任务)
再去做settimeout(宏任务),发现有摇色子2(微任务)。
做完settimeout再去摇色子2。
就是名字取得不好,理解成小任务,大任务,然后先做小任务就好了。
Promise的其他API
Promise.resolve(result)
制造一个成功(或失败)
有的时候,我们需要将所有东西都变成promise
比如说 上面的摇色子,我想造假,每一次都摇出1
如果像之前写的,就很麻烦:
function 摇色子(){const result = 1return new Promise((resolve,reject)=>{resolve(result)})}摇色子().then(n=>console.log(n))// 1
用Promise.resolve就很快
function 摇色子(){return Promise.resolve(1)}摇色子().then(n => console.log(n))//1摇色子().then(n => console.log(n))//1摇色子().then(n => console.log(n))//1
怎么摇都是1,玩晒。
resolve不总接受一个成功,有可能是失败
如果是失败,那么resolve也制造一个失败的promise。
Promise.reject(reason)
制造一个失败
跟resolve差不多,但是他是一定会失败的。
Promise.all(数组)
等待全部成功,或者有一个失败
跟数组的every是差不多的
如果数组里面的全部成功了,就把结果放进一个数组里面
那么失败了呢
它不会管哪个成功了,只要有一个失败,就返回失败。
如果有一个失败的话,后面的失败 他也不管的
所以要获取后面的失败怎么办呢,所以就有下面这个api
Promise.allSettled(数组)
把上面的代码改一改
它会返回一个数组给你,包含对应的promise的状态和返回值
而且它很难失败,所以用成功的回调就可以处理了。
所以就可以用来同时发很多请求,又能把所有结果拿到手。
这个是新的api,很多浏览器不支持
由于这个api的兼容性实在是太差了,很难用在项目中,但是这个api又非常有用,所以我们可以用promise.all伪造一个promise.allSettled
promise.all伪造promise.allSettled
先明白promise.all的特性,他跟allsettled的区别:
主要在于promise.all一旦遇到失败,就不管其他的promise了,所以有失败的情况是拿不到所有的数据的。
那么解决方法就是:把promise.all里面的promise,全部都搞成一定成功
promise().then()是一定返回成功的,所以用then就可以了
let task1 = ()=>new Promise((resolve,reject)=>{setTimeout(()=>{reject(`task1失败了`)},3000)})let task2 = ()=>new Promise((resolve,reject)=>{setTimeout(()=>{resolve(`task2成功了`)},4000)})let task3 = ()=>new Promise((resolve,reject)=>{setTimeout(()=>{reject(`task3失败了`)},5000)})Promise.all([task1().then(()=>({status:'ok'}),()=>({status:'not ok'})),task2().then(()=>({status:'ok'}),()=>({status:'not ok'})),task3().then(()=>({status:'ok'}),()=>({status:'not ok'}))]).then(result => console.log(result))

比如task1,当他失败之后,task1.then()会返回一个成功的对象 叫{status:’not ok’}
这样他就永远不会失败了(then)
如果觉得很麻烦,我们可以写一个promise的转换函数
const transPromise = (promise)=>promise.then((val)=>({status:'ok',val}),(reason)=>({status:'not ok',reason}))Promise.all([transPromise(task1()),transPromise(task2()),transPromise(task3())]).then(result => console.log(result))

还是能继续优化的:
比如tansPromise是接收一个promise list去处理的。 就更简洁了
const transPromise = (promiseList)=>promiseList.map((promise)=>promise.then((val)=>({status:'ok',val}),(reason)=>({status:'not ok',reason})))Promise.all(transPromise([task1(),task2(),task3()])).then(v=>console.log(v))

甚至能直接整一个promise.allSettled
Promise.allSettled2 = function(promiseList){return Promise.all(transPromise(promiseList))}

就跟原版的allSettled一样了。
Promise.race(数组)
发很多个请求,只要有一个请求成功了,就成功了
比如说要备份用户数据,但是不知道备份到哪个服务器,比如说一个中国服务器,一个美国服务器。
所以就同时向中国和美国服务器发请求备份,哪个先成功了就用哪个,另外一个就不要了
