Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

promise阅读笔记 #12

Open
sunyongjian opened this issue Dec 15, 2016 · 3 comments
Open

promise阅读笔记 #12

sunyongjian opened this issue Dec 15, 2016 · 3 comments

Comments

@sunyongjian
Copy link
Owner

sunyongjian commented Dec 15, 2016

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')
})
  • 三种状态
[[PromiseValue]] //内部属性 -- 状态
//而且这个状态不可随意更改,只跟resolve ,reject函数执行有关

实例内部有三种状态标示

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
Copy link

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

@funxinjian
Copy link

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

@pzhu1
Copy link

pzhu1 commented Mar 22, 2018

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants