异步编程 -- Promise

Promise

为了解决回调函数经常出现的回调地狱问题,CommonJS社区提出了Promise的规范,最终在es2015中被标准化,成为语言规范

Promise用来表示一个异步任务最终是成功还是失败,像是对任务作出的承诺,许下这个承诺后,任务进入到pending状态,等待承诺的兑现,然后等承诺兑现后,状态会根据结果发生改变,可能是成功,也可能是失败,但是不再可能是pending等待状态。当状态发生发往后,会执行相应状态的回调函数。

Promise怎么使用?

这里简单用Promise模拟一下ajax

// Promise 方式的ajax
function ajax(url){
  return new Promise(function(resolve,reject){
    var xhr=new XMLHttpRequest()
    xhr.open('GET',url)
    xhr.responseType='json'
    xhr.onload=function(){
      if(this.status===200){
        resolve(this.response)
      }else{
        reject(new Error(this.statusText))
      }
    }
    xhr.send()
  })
}

// 测试
ajax('/api/foo.json')
.then(function(res){
  console.log(res)
})
.catch(function(error){
  console.log(error)
})

Promise常见误区

就拿上面例子举例,当我们从foo.json中获取到数据后,需要再从bar.json中继续获取数据,有时会把代码写成如下格式:

ajax('/api/foo.json')
.then(function(res){
  ajax('/api/bar.json')
  .then(function(res){
    console.log(res)
  }
})

这样写,还是形成了回调地狱,正确的写法可以是下面这样:

ajax('/api/foo.json')
.then(function(res){
  // 在这里处理数据
  // 。。。

  // 把第二个请求进行返回
  return ajax('/api/bar.json')
})
.then(function(res){
  // 这样当我们还需要再进行更多的请求的时候可以接着像刚才这样写
  console.log(res)
  return ajax('/api/other.json')
})
.then(function(res){
  console.log(res)
})

Promise 并行执行

当多个请求之间并没有因为关系时,可以让其同时请求,即并行执行,使用Promise.all和Promise.race可以完成这一要求

// Promise.all需要等全局请求都成功才能获取到结果,结果是一个数组,对应传入请求,失败一个就全部失败,进入catch
var promiseOfAll = Promise.all([
  ajax('/api/foo.json'),
  ajax('/api/bar.json'),
  ajax('/api/other.json')
])
promiseOfAll.then(function(values){
  // values是一个数组
  console.log(values)
})
.catch(function(error){
  console.log(error)
})

// Promise.race只等待第一个完成的请求,任何一个任务完成了,这个Promise就算完成了,全部失败才会进入catch
var promiseOfRace = Promise.race([
  ajax('/api/foo.json'),
  ajax('/api/bar.json'),
  ajax('/api/other.json')
])
promiseOfRace.then(function(value){
  console.log(value)
})
.catch(function(error){
  console.log(error)
})

Promise的静态方法

// 通过resolve方法把常量转换成Promise对象
Promise.resolve('foo')
.then(function(value){
  console.log(value)
})
// 通过reject方法返回一个一定是失败的Promise对象,其传入的数据就是其失败的理由
Promise.reject(new Error('rejected'))
.catch(function(error){
  console.log(error)
})

Promise 执行时序

如下例子:

console.log('global start')
setTimeout(()=>{
  console.log('setTimeout')
},0)
Promise.resolve()
.then(()=>{
  console.log('promise')
})
.then(()=>{
  console.log('Promise2')
})
.then(()=>{
  console.log('Promise3')
})
console.log('end')

输出结果:
global start
end
Promise
Promise2
Promise3
setTimeout

结果分析:首先global start 和end是同步代码,会首先输出,其次是setTimeout和Promise都可以看作是宏任务,宏任务的回调可以是作为一个新的宏任务进入到队列中进行排队,也可以是作为微任务,紧跟着当前宏任务结束后立即执行,而Promise的then、catch、finally都是Promise的微任务,会在Promise之后立即执行,而setTimeout中的回调则是一个新的宏任务,会到队列的未尾重新排队,所以执行时序会靠后

posted @ 2021-06-13 16:35  MissSage  阅读(43)  评论(0编辑  收藏  举报