promise和await async进阶

写博客,既能梳理了专题知识,又加深记忆和理解,好了,不废话,正文马上开始。

1. promise作用

作用:promise解决回调地狱的问题

2.promise基本用法

一定要记得 new Promise(executor) 的 executor 是马上执行的;

promise、then、finally都是微任务;

let myPromise = new promise(function(resolved,rejected){})

Promise结合setTimeout

微任务包括:MutationObserver、Promise.then()或catch()、Promise为基础开发的其它技术,比如fetch API、V8的垃圾回收过程、Node独有的process.nextTick。

宏任务包括:script 、setTimeout、setInterval 、setImmediate 、I/O 、UI rendering。

event loop它的执行顺序:

  1. 一开始整个脚本作为一个宏任务执行
  2. 执行过程中同步代码直接执行,宏任务进入宏任务队列,微任务进入微任务队列
  3. 当前宏任务执行完出队,检查微任务列表,有则依次执行,直到全部执行完
  4. 执行浏览器UI线程的渲染工作
  5. 检查是否有Web Worker任务,有则执行
  6. 执行完本轮的宏任务,回到2,依此循环,直到宏任务和微任务队列都为空

按照event loop规则就能很快输出结果

Promise中的then、catch、finally

  1. Promise的状态一经改变就不能再改变;
  2. .then和.catch都会返回一个新的Promise;
  3. catch不管被连接到哪里,都能捕获上层未捕捉过的错误;
  4. 在Promise中,返回任意一个非 promise 的值都会被包裹成; promise对象,例如return 2会被包装为return Promise.resolve(2);
async function async1 () {
  return 'async1 end'
}
console.log('srcipt start')
async1().then(res => console.log(res))
console.log('srcipt end')

'srcipt start'
'srcipt end'
'async1 end'

复制代码
  1. Promise 的 .then 或者.catch可以被调用多次,但如果Promise内部的状态一经改变,并且有了一个值,那么后续每次调用.then或者.catch的时候都会直接拿到该值;
  2. .then 或者 .catch 中 return 一个 error; 对象并不会抛出错误,所以不会被后续的 .catch 捕获;
  3. .then 或 .catch 返回的值不能是 promise 本身,否则会造成死循环;
  4. .then 或者 .catch 的参数期望是函数,传入非函数则会发生值透传;
Promise.resolve(1)
  .then(2)
  .then(Promise.resolve(3))
  .then(console.log)
  //1  因为发生了透传
复制代码
  1. .then方法是能接收两个参数的,第一个是处理成功的函数,第二个是处理失败的函数,再某些时候你可以认为catch是.then第二个参数的简便写法;
  2. .finally方法也是返回一个Promise,他在Promise结束的时候,无论结果为resolved还是rejected,都会执行里面的回调函数。

就像是这里的finally()会等promise1().then()执行完才会将finally()加入微任务队列

其实你只要记住它三个很重要的知识点就可以了:

  • .finally()方法不管Promise对象最后的状态如何都会执行
  • .finally()方法的回调函数不接受任何的参数,也就是说你在.finally()函数中是没法知道Promise最终的状态是resolved还是rejected的
  • 它最终返回的默认会是一个上一次的Promise对象值,不过如果抛出的是一个异常则返回异常的Promise对象。
const p1 = new Promise((resolve) => {
  setTimeout(() => {
    resolve('resolve3');
    console.log('timer1')
  }, 0)
  resolve('resovle1');
  resolve('resolve2');
}).then(res => {
  console.log(res)
  setTimeout(() => {
    console.log(p1)
  }, 1000)
}).finally(res => {
  console.log('finally', res)
})

'resolve1'
'finally' undefined
'timer1'
Promise{<resolved>: undefined}

分析:
- Promise的状态一旦改变就无法改变
- finally不管Promise的状态是resolved还是rejected都会执行,且它的回调函数是接收不到Promise的结果的,所以finally()中的res是一个迷惑项(类似3.10)。
- 最后一个定时器打印出的p1其实是.finally的返回值,我们知道.finally的返回值如果在没有抛出错误的情况下默认会是上一个Promise的返回值, 
- 而这道题中.finally上一个Promise是.then(),但是这个.then()并没有返回值,所以p1打印出来的Promise的值会是undefined,如果你在定时器的下面加上一个return 1,返回是1。


复制代码

Promise中的all、race

  • Promise.all()的作用是接收一组异步任务,然后并行执行异步任务,并且在所有异步操作执行完后才执行回调。
  • .race()的作用也是接收一组异步任务,然后并行执行异步任务,只保留取第一个执行完成的异步操作的结果,其他的方法仍在执行,不过执行结果会被抛弃。
  • Promise.all().then()结果中数组的顺序和Promise.all()接收到的数组顺序一致。
  • all和race传入的数组中如果有会抛出异常的异步任务,那么只有最先抛出的错误会被捕获,并且是被then的第二个参数或者后面的catch捕获;但并不会影响数组中其它的异步任务的执行。

3. await async

正常情况下,async中的await命令是一个Promise对象,返回该对象的结果。

但如果不是Promise对象的话,就会直接返回对应的值,相当于Promise.resolve()

async function fn () {
  // return await 123
  // 等同于
  return 123
}
fn().then(res => console.log(res)) //123
复制代码
async function async1 () {
  console.log('async1 start');
  await new Promise(resolve => {
    console.log('promise1')
  })
  console.log('async1 success');
  return 'async1 end'
}
console.log('srcipt start')
async1().then(res => console.log(res))
console.log('srcipt end')

在async1中await后面的Promise是没有返回值的,也就是它的状态始终是pending状态,
因此相当于一直在await,await,await却始终没有响应...,
所以在await之后的内容是不会执行的,也包括async1后面的 .then。

'script start'
'async1 start'
'promise1'
'script end'
复制代码

注意

async function async1 () {
  await new Promise(resolve => {
    console.log('promise1')
    resolve('promise resolve')
  })
  console.log('async1 success');
  return 'async1 end'
}
console.log('srcipt start')
async1().then(res => {
  console.log(res)
})
这道过有一点需要注意的,在async1中的newPromise它的resovle的值和async1().then()里的值是没有关系的,小伙伴可能看到resovle('promise resolve')就会误以为是async1().then()中的返回值。

'script start'
'promise1'
'async1 success'
'async1 end'  //此处是返回值,不是resovle('promise resolve')

复制代码

await 的异常处理

如果在async函数中抛出了错误,则终止错误结果,不会继续向下执行。

以下例子使用reject和Error,都中断执行

async function async1 () {
  await async2();
  console.log('async1');
  return 'async1 success'
}
async function async2 () {
  return new Promise((resolve, reject) => {
    console.log('async2')
    reject('error')
  })
}
async1().then(res => console.log(res))

'async2'
Uncaught (in promise) error
复制代码

改为throw new Error

async function async1 () {
  console.log('async1');
  throw new Error('error!!!')
  return 'async1 success'
}
async1().then(res => console.log(res))
复制代码

如果想要使得错误的地方不影响async函数后续的执行的话:

  • 可以使用try catch
  • 可以直接在Promise.reject后面跟着一个catch()方法:
async function async1 () {
  // try {
  //   await Promise.reject('error!!!')
  // } catch(e) {
  //   console.log(e)
  // }   //此方法也行
  await Promise.reject('error!!!')
    .catch(e => console.log(e))
  console.log('async1');
  return Promise.resolve('async1 success')
}
async1().then(res => console.log(res))
console.log('script start')

'script start'
'error!!!'
'async1'
'async1 success'
此处注意catch执行在console.log('async1')之前,不像then的执行顺序
复制代码

await用法的缺点和优点

优点:

  • 同步代码思想
  • 写法上简单

缺点:

  • 有些执行没必要等待

4. Promise和await async

  • Promise结合await async

记住紧跟着await后面的语句相当于放在了new Promise中,下一行及之后的语句相当于放在于Promise.then中

async function async1() {
  console.log("async1 start");
  await async2();
  console.log("async1 end");
}
async function async2() {
  console.log("async2");
}
async1();
console.log('start')

复制代码
  • Promise和await async的区别

Promise不会阻塞后面同步代码的执行,await会阻塞

async function async1() {
  console.log("async1 start");
  await async2();
  console.log("async1 end");
}
async function async2() {
  setTimeout(() => {
    console.log('timer')
  }, 0)
  console.log("async2");
}
async1();
console.log("start")

执行结果
'async1 start'
'async2'
'start'
'async1 end'
'timer'
定时器始终还是最后执行的,它被放到下一条宏任务的延迟队列中。
复制代码

5. promise手写代码

  1. v1.0 初始版本myPromise
function myPromise(contructor){
    let self = this
    self.status = "pending";
    self.value = undefinded;
    self.reason = undefinded;
    
    function resolved(value){
        if(self.status === 'pending'){
            self.status = 'resolved';
            self.value = value;
        }
    }
    function rejected(){
        if(self.status === 'pending'){
            self.status = 'rejected';
            self.reason = reason;
        }
    }
    contructor(resolved,rejected)
}
myPromise.prototype.then = function(onFullFilled,onRejected){
    let self = this;
    switch(this.status){
        case 'resolved':
            onFullFilled(self.value)
        case 'rejected':
            onRejected(self.value)
    }
}
复制代码
  1. v2.0 基于观察者模式实现

用2个数组onFullfilledArray和onRejectedArray来保存异步的方法。在状态发生改变时,一次遍历执行数组中的方法。

function myPromise(contructor){
    let self = this
    self.status = "pending";
    self.value = undefinded;
    self.reason = undefinded;
    self.onFullfilledArray = [];
    self.onRejectedArray = [];
    
    function resolved(value){
        if(self.status === 'pending'){
            self.status = 'resolved';
            self.onFullfilledArray.forEach(function(f){
                f(self.value)
                //如果状态从pending变为resolved,那么就遍历执行里面的异步方法
            })
            
        }
    }
    function rejected(){
        if(self.status === 'pending'){
            self.status = 'rejected';
            self.onRejectedArray.forEach(function(f){
                f(self.reason)
                //如果状态从pending变为rejected,那么就遍历执行里面的异步方法
            })
        }
    }
    contructor(resolved,rejected)
}
myPromise.prototype.then = function(onFullFilled,onRejected){
    let self = this;
    switch(this.status){
        case 'pending':
            self.onFullfilledArray.push(function(){
                onFullFilled(self.value)
            }
            self.onRejectedArray.push(function(){
                onFullFilled(self.reason)
            }
        case 'resolved':
            onFullFilled(self.value)
            break;
        case 'rejected':
            onRejected(self.reason)
            break;
        default;
    }
}
复制代码

通过两个数组,在状态发生改变之后再开始执行,这样可以处理异步resolve无法调用的问题。这个版本的myPromise就能处理所有的异步,那么这样做就完整了吗?

  1. v3.0 then方法实现链式调用
myPromise.prototype.then = function(onFullFilled,onRejected){
    let self = this;
    let newPromise;
    switch(this.status){
        case 'pending':
            <!--then返回promise-->
            return newPromise = new myPromise(function(resolved,rejected){
                self.onFullfilledArray.push(function(){
                    try{
                       let temple=onFullfilled(self.value); //得到resolved的值
                       resolve(temple)
                    }catch(e){
                       reject(e) //error catch
                    }
                });
                self.onFullfilledArray.push(function(){
                    try{
                       let temple=onRejected(self.reason); //得到rejected的值
                       reject(temple)
                    }catch(e){
                       reject(e) //error catch
                    }
                });
            })
        case 'resolved':
            return newPromise = new myPromise(function(resolve,reject){
                try{
                  let temple=onFullfilled(self.value);
                  //将上次一then里面的方法传递进下一个Promise的状态
                  resolve(temple);
                }catch(e){
                  reject(e);//error catch
                }
            })
            break;
        case 'rejected':
            return newPromise = new myPromise(function(resolve,reject){
            try{
               let temple=onRejected(self.reason);
               //将then里面的方法传递到下一个Promise的状态里
               resolve(temple);   
            }catch(e){
               reject(e);
            }
        })
            break;
        default;
    }
}

复制代码

这样通过then方法返回一个promise就可以实现链式的调用:

p.then(function(x){
    console.log(x)
})
.then(function(){
    console.log("链式调用1")
})
.then(function(){
    console.log("链式调用2")
})
//输出
1
链式调用1
链式调用2
复制代码

这样我们虽然实现了then函数的链式调用,但是还有一个问题,就是在Promise/A+规范中then函数里面的onFullfilled方法和onRejected方法的返回值可以是对象,函数,甚至是另一个promise。 疑问:以上onFullfilled方法和onRejected方法有返回值吗?

4.v4.0 then函数中的onFullfilled和onRejected方法的返回值问题

function resolvePromise(promise,x,resolve,reject){
  if(promise===x){
     throw new TypeError("type error")
  }
  let isUsed;
  if(x!==null&&(typeof x==="object"||typeof x==="function")){
      try{
        let then=x.then;
        if(typeof then==="function"){
           //是一个promise的情况
           then.call(x,function(y){
              if(isUsed)return;
              isUsed=true;
              resolvePromise(promise,y,resolve,reject);
           },function(e){
              if(isUsed)return;
              isUsed=true;
              reject(e);
           })
        }else{
           //仅仅是一个函数或者是对象
           resolve(x)
        }
      }catch(e){
         if(isUsed)return;
         isUsed=true;
         reject(e);
      }
  }else{
    //返回的基本类型,直接resolve
    resolve(x)
  }
}
复制代码

改变了resolvePromise函数之后,我们在then方法里面的调用也变成了resolvePromise而不是promise。

myPromise.prototype.then=function(onFullfilled,onRejected){
    let self=this;
    let promise2;
    switch(self.status){
      case "pending":
        promise2=new myPromise(function(resolve,reject){
             self.onFullfilledArray.push(function(){
                setTimeout(function(){
                  try{
                       let temple=onFullfilled(self.value);
                       resolvePromise(temple)
                    }catch(e){
                       reject(e) //error catch
                    }
                })
             });
             self.onRejectedArray.push(function(){
                setTimeout(function(){
                   try{
                       let temple=onRejected(self.reason);
                       resolvePromise(temple)
                     }catch(e){
                       reject(e)// error catch
                   }
                })
             });
        })
      case "resolved":
        promise2=new myPromise(function(resolve,reject){
           setTimeout(function(){
               try{
                  let temple=onFullfilled(self.value);
                  //将上次一then里面的方法传递进下一个Promise状态
                  resolvePromise(temple);
                }catch(e){
                  reject(e);//error catch
               }
           })
        })
        break;
      case "rejected":
        promise2=new myPromise(function(resolve,reject){
           setTimeout(function(){
             try{
               let temple=onRejected(self.reason);
               //将then里面的方法传递到下一个Promise的状态里
               resolvePromise(temple);   
             }catch(e){
               reject(e);
             }
           })
        })
        break;
      default:       
   }
   return promise2;
}
复制代码

6. 如何让异步顺序执行

var arr = [1, 2, 3, 4]
var promises = []

arr.map(async (value) => {
  promises.push(new Promise((res) => {
    setTimeout(() => {
      console.log(value)
      res()
    }, 1000)
  }))
})

var promise = Promise.resolve()

for (var i = 0; i < promises.length; i += 1) {
  const task = promises[i];
  promise.then(() => {
      return task
    })
}
复制代码
arr.reduce((p, x) => p.then(() => new Promise(r => setTimeout(() => r(console.log(x)), 1000))), Promise.resolve())
复制代码
const arr = [1, 2, 3]
arr.reduce((p, x) => {
  return p.then(() => {
    return new Promise(r => {
      setTimeout(() => r(console.log(x)), 1000)
    })
  })
}, Promise.resolve())
复制代码

注意掌握reduce的高级用法,此处我还得多写几个例子加深理解

7. 场景题目

使用Promise实现每隔1秒输出1,2,3

做此题先写循环方法代码,不能满足要求

function print(){
    for (let i=1;i<4;i++){
        console.log('first:',i)
        new Promise(()=>{
            console.log('second:',i)
            setTimeout(function(){
                console.log(i++)
            },1000)
        })
    }
}
print() 

async function print(){
  let a = [1,2,3]
  for (let i of a){
    console.log('first',i)
    await new Promise((resolved)=>{
      resolved(i)
    }).then(()=>{
    console.log('second:',i)
      setTimeout(function(){
          console.log(i)
      },1000)})
}

}
print()

//都是先输出
> "first" 1
> "second:" 1
> "first" 2
> "second:" 2
> "first" 3
> "second:" 3
再1s后同时并行输出
> 1
> 2
> 3
原因:在一个循环作用域下,执行异步

new Promise是微任务,setTimeout是宏任务
主程序完成之后,执行微任务,宏任务压入执行队列中
复制代码
async function print(){
  let a = [1,2,3]
  for (let i of a){
    console.log('first',i)
    await new Promise((resolved)=>{
      resolved(i)
    }).then(()=>{
    console.log('second:',i)
     // setTimeout(function(){
          console.log(i)
     // },1000)
    })
    }
}


> "first" 1
> "second:" 1
> 1
> "first" 2
> "second:" 2
> 2
> "first" 3
> "second:" 3
> 3
复制代码
function print(i){
  return new Promise((resolved)=>{
      setTimeout(function(){
          console.log(i)
            resolved(i)
      },2000)
      
    })
    


}
print(1).then(()=>{
    print(2)
}).then(()=>{
    print(3)
})

2s后先出现1,再出现2,3,因为setTimeout最后面执行
> 1
> 2
> 3
复制代码
function print(i){
  return new Promise((resolved)=>{
      setTimeout(function(){
          console.log(i)
            resolved(i)
      },2000)
      
    })
    
}
async function a(i){
  await print(i)
}
a(1)
a(2)
a(3)

2s后同时出现
> 1
> 2
> 3
复制代码
var arr = [1, 2, 3, 4]
var promises = []

arr.map(async (value) => {
  promises.push(new Promise((res) => {
    setTimeout(() => {
      console.log(value)
      res()
    }, 2000)
  }))
})

var promise = Promise.resolve()


for (var i = 0; i < promises.length; i += 1) {
  const task = promises[i]
  promise
    .then(() => {
      return task
    })
}

2s后同时出现
> 1
> 2
> 3
> 4
复制代码

把let改成var

function print(){
    for (var i=1;i<4;i++){
        console.log('first:',i)
        new Promise(()=>{
            console.log('second:',i)
            setTimeout(function(){
                console.log(i++)
            },1000)
        })
    }
}
print() 
//先输出
> "first" 1
> "second:" 1
> "first" 2
> "second:" 2
> "first" 3
> "second:" 3
再1s后同时并行输出
> 4
> 5
> 6

因为先执行了for循环,再执行new Promise

当异步事件发生时,会创建事件并放入执行队列中,等待当前代码执行完成之后再执行这些代码。
复制代码

如何让异步操作顺序执行 最终代码:

const arr = [1, 2, 3]
arr.reduce((p, x) => {
  return p.then(() => {
    return new Promise(r => {
      setTimeout(() => r(console.log(x)), 1000)
    })
  })
}, Promise.resolve())

部分变成非箭头函数
const arr = [1, 2, 3]
arr.reduce((p, x) => {
  return p.then(() => {
    return new Promise(function(resolved){
        setTimeout(function(){
          resolved(console.log(x))
          //console.log(x)  如果没有resolved()包含console.log(x),则仅仅1s后输出1
        }, 1000)
    })
  })
}, Promise.resolve())

要知道resolved是个函数

每隔1s后输出
> 1
> 2
> 3
复制代码

红灯3秒亮一次,黄灯2秒亮一次,绿灯1秒亮一次,使用Promise实现红绿灯交替重复亮?

function red() {
    console.log('red');
}
function green() {
    console.log('green');
}
function yellow() {
    console.log('yellow');
}


new Promise((resolve)=>{
    setTimeout(()=>{
        red()
        resolve()
    },3000)
}).then((resolve) => {
    setTimeout(()=>{
        green()
        //resolve()  
    },2000)
  }).then((resolve) => {
    setTimeout(()=>{
        yellow()
     // resolve()
    },1000)
  })
  
> "red"
> "yellow"
> "green"


报错:VM5613:22 Uncaught TypeError: resolve is not a function
要知道then后面没有resolved函数

复制代码

下面换return方式试:

function print(timer,cb){
    return new Promise(function(resolve){
        setTimeout(() => {
          cb()
          resolve()
        }, timer)
    })
} 
Promise.resolve().then(() => {
    return print(3000, red)  
}).then(() => {
    return print(2000, green)
  }).then(() => {
    return print(1000, yellow)
  })
 
 3s后输出"red",2s后输出"green",1s后输出"yellow"
> "red"
> "green"
> "yellow"

我自己的解释:Promise.resolve方法允许调用时不带参数,直接返回一个resolved状态的 Promise 对象,所以回调函数会立即执行;then()函数里不返回值或者返回的不是promise,那么 then 返回的 Promise 将会成为接受状态(resolve)

`都是在then之后返回print,也就是new Promise`

new Promise(()=>{
    return print(3000, red)
}).then(() => {
    return print(2000, green)
 }).then(() => {
    return print(1000, yellow)
})



3s后输出
> "red"

new Promise((resolve)=>{
    //return new Promise(function(resolve){
        setTimeout(() => {
          red()
          resolve()
        }, 3000)
    //})
}).then(() => {
    return new Promise(function(resolve){
        setTimeout(() => {
          green()
          resolve()
        }, 2000)
    })
 }).then(() => {
    return new Promise(function(resolve){
        setTimeout(() => {
          yellow()
          resolve()
        }, 2000)
    })
})
注释then之前的return,为了获取resolve状态,这个很能对比Promise.resolve()

> 3s后输出"red",2s后输出"green",1s后输出"yellow"
复制代码

这个想象让我问自己为什么第一行必须写Promise.resolve()

来了解下new Promise()和Promise.resolve()的区别

Promise.resolve()

1)如果参数是一个Promise实例,Promise.resolve将不做任何修改、原封不动地返回这个实例


let v = new Promise(resolve => {
    console.log("begin");
    //resolve("then");
});
Promise.resolve(v)

> "begin"

let v = new Promise(resolve => {
    console.log("begin");
    resolve("then");
});
Promise.resolve(v).then((res)=>console.log(res))
    
> "begin"
> "then"
复制代码

2)如果参数是一个包含then的对象

Promise.resolve方法会将这个对象转为 Promise 对象实例

let thenable = {
    then: function(resolve, reject) {
        resolve(42);
    }
}
let p1 = Promise.resolve(thenable);
p1.then(function(value) {
  console.log(value);  // 42
});
复制代码

3)参数不是具有then方法的对象,或根本就不是对象,也不是Promise实例

Promise.resolve方法返回一个新的 Promise 对象,状态为resolved

const p = Promise.resolve('HHY');

console.log(p) //Promise {<resolved>: "HHY"}
 
p.then(function (s){
  console.log(s)
});

> HHY

复制代码

4)不带任何参数

直接返回一个resolved状态的 Promise 对象


console.log(Promise.resolve()) //Promise {<resolved>: undefined}

setTimeout(function () {
  console.log('three');
}, 0);
 
Promise.resolve().then(function () {
  console.log('two');
});
 
console.log('one');
 
> "one"
> "two"
> "three"

复制代码

.then()函数里不返回值或者返回的不是promise,那么 then 返回的 Promise 将会成为接受状态(resolve)

Promise.resolve()
.then(() => console.log(2))
.then(() => console.log(3)
);
console.log(1); 

> 1
> 2
> 3
复制代码

先输出1的原因:

Promise.resolve(),是在本轮“事件循环”(event loop)的结束时执行,不是马上执行,也不是在下一轮“事件循环”的开始时执行.

原因:传递到 then() 中的函数被置入了一个微任务队列,而不是立即执行,这意味着它是在 JS 事件队列的所有运行时结束了,事件队列(我的理解是同步代码的事件队列)被清空之后,才开始执行.

new Promise()

let v = new Promise(resolve => {
  console.log("begin");
  resolve("then");
});

复制代码

在promise里面resolve一个状态为fulfilled的promise

resolve()本质作用:

resolve()是用来表示promise的状态为fullfilled,相当于只是定义了一个有状态的Promise,但是并没有调用它; promise调用then的前提是promise的状态为fullfilled; 只有promise调用then的时候,then里面的函数才会被推入微任务中;

区别

  • new promise是返回一个promise对象
  • Promise.resolve() 返回一个promise对象,状态为resolved
  • Promise.resolve方法允许调用时不带参数,直接返回一个resolved状态的 Promise 对象,所以回调函数会立即执行;then()函数里不返回值或者返回的不是promise,那么 then 返回的 Promise 将会成为接受状态(resolve)

因此new promise的操作就跟你 new 一个普通函数没区别,所以这一句其实是宏任务,但后面的then是微任务

resolved后的promise对象会在这该级别事件队列结束之后才开始执行,及执行与该轮微任务队列中,始于下一级别宏任务之前

Promise.resolve()可以返回实例对象。

Promise.resolve(v)不等于new Promise(r => r(v))

当v是一个Promise实例的时候就会出现一些不同的地方

    // v是一个实例化的promise,且状态为fulfilled
    let v = new Promise(resolve => {
      console.log("begin");
      resolve("then");
    });
 
 
    模式一 new Promise里的resolve()
    
    结果: begin->1->2->3->then->4 可以发现then推迟了两个时序
    推迟原因:浏览器会创建一个 PromiseResolveThenableJob 去处理这个 Promise 实例,这是一个微任务。
    等到下次循环到来这个微任务会执行,也就是PromiseResolveThenableJob 执行中的时候,因为这个Promise 实例是fulfilled状态,所以又会注册一个它的.then()回调
    又等一次循环到这个Promise 实例它的.then()回调执行后,
    才会注册下面的这个.then(),于是就被推迟了两个时序
    new Promise(resolve => {
      console.log(v) //Promise {<resolved>: "then"}
      console.log(resolve(v)) //undefined
      resolve(v);
    }).then((v)=>{
        console.log(v) //获取then值得地方
    });  //begin->1->2->3->then->4 
    
    我的理解:resolve中包含实例对象,先输出begin,再执行下面输出1,2,3(1理解,为什么会执行了2和3,再then呢?),再来执行获取then值得地方
 
    模式二 Promise.resolve(v)直接创建
    
    结果:begin->1->then->2->3->4 可以发现then的执行时间正常了,第一个执行的微任务就是下面这个.then
    原因:Promise.resolve()API如果参数是promise会直接返回这个promise实例,不会做任何处理

     Promise.resolve(v).then((v)=>{
        console.log(v)
    }); //begin->1->then->2->3->4
 
    new Promise(resolve => {
      console.log(1);
      resolve();
    })
      .then(() => {
        console.log(2);
      })
      .then(() => {
        console.log(3);
      })
      .then(() => {
        console.log(4);
      });

> "begin"
> 1
> "then"
> 2
> 3
> 4


> "begin"
> 1
> 2
> 3
> "then"
> 4

复制代码

实现mergePromise函数

把传进去的数组按顺序先后执行,并且把返回的数据先后放到数组data中。

const time = (timer) => {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve()
    }, timer)
  })
}
const ajax1 = () => time(2000).then(() => {
  console.log(1);
  return 1
})
const ajax2 = () => time(1000).then(() => {
  console.log(2);
  return 2
})
const ajax3 = () => time(1000).then(() => {
  console.log(3);
  return 3
})

function mergePromise (ajaxArray) {
  // 存放每个ajax的结果
  const data = [];
  let promise = Promise.resolve();
  ajaxArray.forEach(ajax => {
    // 第一次的then为了用来调用ajax
    // 第二次的then是为了获取ajax的结果
    promise = promise.then(ajax).then(res => {
      data.push(res);
      return data; // 把每次的结果返回
    })
  })
  // 最后得到的promise它的值就是data
  return promise;

}

mergePromise([ajax1, ajax2, ajax3]).then(data => {
  console.log("done");
  console.log(data); // data 为 [1, 2, 3]
});


复制代码

根据promiseA+实现一个自己的promise

封装一个异步加载图片的方法

,只需要在图片的onload函数中,使用resolve返回一下就可以了。

function loadImg(url) {
  return new Promise((resolve, reject) => {
    const img = new Image();
    img.onload = function() {
      console.log("一张图片加载完成");
      resolve(img);
    };
    img.onerror = function() {
        reject(new Error('Could not load image at' + url));
    };
    img.src = url;
  });


复制代码

限制异步操作的并发个数并尽可能快的完成全部

8. 参考链接

github.com/fortheallli…

juejin.im/post/684490…

9. 后语

花了一周时间整理,一些标记是自己不熟悉的地方,另外一些升华题需要继续研究,有时间继续补充和更正,请大家阅读,有收获请给个👍

posted @ 2020-09-04 11:09  威武的大萝卜  阅读(178)  评论(0编辑  收藏  举报