sunyongjian / blog

个人博客😝😋😄
664 stars 54 forks source link

promise阅读笔记 #12

Open sunyongjian opened 7 years ago

sunyongjian commented 7 years ago

promise

为什么会有promise

根据官方的说法,promise的出现是为了解决"callback hell"回调地狱, 将横向的嵌套回调改为可以纵向以then的方式加载执行。

简介 & 用法

// 通过new Promise 得到一个promise的实例,也就是一个普通对象。构造函数需要传一个callback 去定义何时执行resolve,reject 这两个函数
var promise = new Promise(function(resolve, reject) {
  if('code') { // code 可能是异步操作成功判断条件
    resolve()
  } else {
    reject()
  }
})
// then的两个参数(resolve, reject)
promise.then(function(){
  console.log('success')
},function(){
  console.log('failure')
})

Pending 进行中 即构造函数执行开始

Resolved 已成功 resolve函数 执行

Rejected 已失败 reject函数 执行

而且只能由Pending --> Resolved,Pending-->Rejected ,即成功或失败

这个状态变化的条件是由我们控制的。一个请求返回成功状态码... 等等

Promise.prototype.then()

两个参数 resolve, reject

function getPromise (status) {
   return new Promise(function(resolve, reject) {
    let time = Date.now()
    console.log('time')
    setTimeout(()=>{
       if(status) {
         resolve(time)
       } else {
         reject(new Error('status error'))
       }
    },1000)    
  })
}

getPromise(1).then((time)=>{
  console.log('1',Date.now()-time)
  return Date.now()-time
},(err)=>{
  console.log(err,'err')
  return err
}).then((time)=>{
  console.log('2',time)
  return time
},(err)=>{
  console.log(err,'err')
  return err
}).then((time)=>{
  console.log('3',time)
},(err)=>{
  console.log(err,'err')
})

//
"time"
"1" 1002
"2" 1002
"3" 1002
//
// status传0
getPromise(0).then((time)=>{
  console.log('1',Date.now()-time)
  return Date.now()-time
}, (err)=>{
  console.log(err,'err')
  return err
}).then((time)=>{
  console.log('2',time)
  return time
},(err)=>{
  console.log(err)
  return err
}).then((time)=>{
  console.log('3',time)
},(err)=>{
  console.log(err)
})
//
"time"
[object Error] { ... } "err"
"2" [object Error] { ... }
"3" [object Error] { ... }
// 很有意思的是第一次promise 异步代码失败
执行reject, 但是后面的都是执行resolve,而且resolve的参数都已经是error对象了
  1. 这里我们定义了一个函数来返回一个promise实例,然后实例可以执行then。只要构造函数执行,里面的代码就会执行,异步代码也会放到异步队列,当异步代码执行完(这里就是我们的setTimeout),根据我们提供的条件,成功执行resolve,失败就reject。当然条件都是我们自己规定的,比如异步请求的code...

  2. 只要异步代码成功,我们的条件成功,就可以无限then。因为then传的resolve,reject 执行完都后,都会返回一个promise实例。另外,如果我们在resolve里return一个新的promise实例,那么这个promise实例将作为新的then的调用者。如果return一个非promise实例,都会作为参数传递给下面的resolve..

  3. 无论是成功的结果还是error信息,都会被一级级传递下来。利用此,举个栗子我们有三个请求,每个请求的参数都依赖于上一个请求的结果,用promise就很简洁... 另外收集错误也是可以做到的。

举个栗子

var getJson = function (url) {
  return new Promise(function (resolve, reject) {
    var xhr = new XMLHttpRequest ()
    xhr.open('get',url)
    xhr.onreadystatechange = callback
    xhr.responseType = 'json'
    xhr.send(null)
    function callback () {
      if(this.readyState !== 4) return
      if(this.status === 200) {
        resolve(this.response)
      }else{
        reject(new Error(this.statusText))
      }
    }
  })
}

getJson('api/list').then((response)=>{
    console.log(response)
    return getJson(response.url)
},(err)=>{
  console.log(err)
  return err
}).then((response)=>{
    console.log(response)
},(err)=>{
  console.log(err)
})
//这个就是典型的第二个请求的参数依赖于第一个请求返回的结果。

业务中还有那种比如两三个请求都成功了,我们才处理,渲染的。用promise都比较好处理
Promise.prototype.catch()

Promise.prototype.catch方法是.then(null, rejection)的别名,用于指定发生错误时的回调函数。

getPromise(0).then((time)=>{
  console.log('1',Date.now()-time)
  return Date.now()-time
},(err)=>{
  console.log(err,'err')
  return err
}).then(null,(err)=>{
  console.log(err,'err')
  return err
}).then(null,(err)=>{
  console.log(err,'err')
})

类似于我们上面的例子把后面两个then 的 resolve去掉 改为null. 也就是说,当我们需要在第二个then 里面有目的的收集错误的时候, 我们可以直接用catch方法,省去then写法的麻烦。 类似于这样

getPromise(0).then((time)=>{
  console.log('1',Date.now()-time)
  return Date.now()-time
},(err)=>{
  console.log(err,'err')
  return err
}).catch((err)=>{
  console.log(err,'err')
  return err
}).catch((err)=>{
  console.log(err,'err')
})

当然我们不会写两个catch。这里只是改写一下then。 更多详细的直接看es6 入门里面,讲的很详细,因为catch我用的也不是很多 es6入门阮一峰-promise

Promise.all()

all 的主要用途就是多个请求,同时成功了,再做某事 参数是一个数组,数组里面是Promise实例

注意这个方法是构造函数上的方法,用于将多个Promise实例,包装成一个新的Promise实例

两个Promise,请求 1.getExams  2.getQuestion
Promise.all([getExams(examId), getQuestion(questionId)]).then((exams,questions)=>{
    console.log(exams,questions)
})

应用场景就是我们多个请求都成功,才能做什么。

Promise.race()

跟all用法类似,都是传多个promise对象。根据race 的意思,我们大概知道是干嘛的了。哪个先完成,就返回哪个的数据。 应用场景大概是两个服务器,比较哪个快。但是现在服务器 都会通过nginx 做负载均衡,也不需要前端去关注这些东西。

Promise.race([getExams(examId), getQuestion(questionId)]).then((data)=>{
    console.log(data)
})

race还要一个应用场景就是可以做超时处理。

const timeOut = 3000;
const delay = function delay(time) {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve(JSON.stringify({
        code: 0,
        msg: '请求超时',
      }));
    }, time);
  });
};

const response = Promise.race([delay(timeOut), fetch('api/list')]);

当fetch超过3秒,delay的promise对象resolve(), 结果就会赋值给response,此时的response读取到的已经是超时的。但是还是无法abort请求... 这算是promise的一个痛点

本次笔记记录先到此,其实API 书中都比较清楚了。关键还是项目中的应用。

推荐链接

es6入门阮一峰-promise javascript标准参考教材 剖析Promise内部结构

guoChopper commented 7 years ago

阮一峰的文章比较详细,博主挑一些主要的做了总结,再看一遍增加记忆了,感谢

funxinjian commented 7 years ago

总结的很不错,感谢博主的分享。。

pzhu1 commented 6 years ago

不错,我写过源代码分析: http://www.cnblogs.com/pzhu1/p/8365963.html