博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
异步解决方案一:promise
阅读量:6735 次
发布时间:2019-06-25

本文共 9163 字,大约阅读时间需要 30 分钟。

之前一直对于promise,就是一顿用,从来没有深究其内部原理,无意间看到一些关于promise输出顺序的题目也是一顿瞎猜。。。哈哈,在我的同事对promise进行分享之后(虽然我因为起不来没有听),但是学习promise的意识就此种下。。。啥也不说了,就是学。。。

带着疑问去学习:

  1. promise是干什么的?
  2. promise有哪些方法?
  3. pomise如何实现链式调用?之前jquery也是可以链式调用,他们之前的区别?
  4. promise的then方法是如何确定在前面resolve或者reject执行完拿到结果再执行的?
  5. 手写一个满足promiseA+的promise方法(写了一下,发现无法通过promiseA+的测试用例,已放弃,拿着别人写的仔细看了一下)

promise是干什么的

promise是一种异步编程的解决方案,它可以实现把异步操作按照同步操作的流程表达出来。它可以接收一个异步请求,然后在其then方法可以得到前面异步请求的结果进行后续操作,使用链式调用的方式将异步请求的结果按照同步的方式表达出来。

理解误区

1. promise只是异步编程的解决方案,自身并不是异步请求,在new Promise()内部传的方法会立即执行。

const promise = new Promise(function (resolve, reject) {  console.log('12')  setTimeout(function () {    resolve('success')  }, 1000)})promise.then(function (success) {  console.log(success)})// 执行结果: 先输出12,过了一秒后再输入success复制代码

2. new Promise内的resolve是异步执行的,且所有then方法和catch方法也会将在当前脚本所有同步任务执行完才会执行

const myPromise = new Promise(function (resolve, reject) {  console.log('1')  resolve('2')  console.log('3')})myPromise.then(function (success) {  console.log(success)})setTimeout (function () {    // 这里可以看到前面myPromise已经resolved了,但是他还是会在最后执行,因为then内部也是异步操作    myPromise.then(function (success) {        console.log('again', success)    })    console.log('4')}, 1000)console.log('5')// 执行结果是: 1 3 5 2 4 again 2复制代码

promise有哪些方法

基本用法

const promise = new Promise(function(resolve, reject) {  if (/* 异步操作成功 */){    resolve(value);  } else {    reject(error);  }});promise.then(function(value) {}, function (error) {})复制代码

基本用法简单概括一下:

  1. 三种状态: - pending(进行中) - fulfilled(已成功) -rejected(已失败)
  2. new Promise传进的方法内resolve返回的是成功的结果,reject返回的是失败的结果
  3. new Promise实例化后的then方法接受两个回调函数,分别是resolve和reject的回调函数

挂载在原型上的方法

Promise.prototype.then()&&Promise.prototype.catch()

  • then: 该方法内的函数在Promise状态为resolve的情况下执行,且then方法内属于异步操作,如果前面的new Promise没有resolve,then内部传进来的函数不执行
  • catch:该方法内的函数在Promise状态为reject的情况下执行,且catch方法内属于异步操作,catch方法和then不太一样,它在前面new Promise()出错或者reject时,catch内部的方法都会执行。

then()和catch()默认返回的都是一个Promise实例对象,即使return了别的信息,返回的仍然是Promise实例对象,还是且这个promise的状态都是resolve

Promise.prototype.finally()

不管Promise对象最后状态如何,finally方法都会执行,(promise是pedding状态不会执行finally方法,resolve和reject都会执行)

挂载在Promise对象上的方法

Promise.resolve() && Promise.reject()

将现有对象转换为Promise对象

Promise.all()

将多个Promise实例包装成一个新的Promise实例。

var p1 = new Promise((resolve) => {  setTimeout(() => {    resolve('p1')  }, 1000)})var p2 = new Promise((resolve) => {  setTimeout(() => {    resolve('p2')  }, 2000)})var p3 = new Promise((resolve) => {  setTimeout(() => {    resolve('p3')  }, 3000)})var p = Promise.all([p1, p2, p3]).then((value) => {  console.log(value)}).catch((err) => {  console.log(err)})// 返回值:['p1', 'p2', 'p3']复制代码

这里Promise的状态是由p1,p2,p3共同决定的:

  1. p1,p2和p3的状态都为resolve时,p1,p2和p3的返回值都以一个数组的形式传给后续p的then方法里
  2. 其中有一个的状态变为reject时,第一个被reject的实例的返回值会传递给后续p的catch方法里
  3. 如果前面作为参数传进来的promise实例中状态为rejected了,且其自己定义了catch方法,那么后续不会触发Promise.all()的then方法
  4. 如果前面作为参数传进来的promise实例有状态为rejected,且其有catch方法,那么就不会出发后面的Promise.all()的catch方法,会触发then()方法。

Promise.race()

将现有对象转换为Promise对象,只要p1、p2、p3之中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给p的回调函数。

pomise如何实现链式调用?之前jquery也是可以链式调用,他们之前的区别?

promise如何实现链式调用

在每一次执行Promise的then或者catch方法后,返回的都是一个新的Promise实例化对象,使得又一次可以使用then或者catch方法,并且可以拿到上一次then方法return回来的值。

例如:

then内部的函数return了一个数值a。我们发现then方法执行完,返回的是一个新的promise实例,其promisevalue为a

根据以上铺垫,我们就知道,其链式调用就是通过then和catch方法内部return new Promise()

jquery如何实现链式调用

通过return this,在其原型方法执行结束都会teturn其本身

var Jquery = function () {}Jquery.prototype = {  css: function (attr, data) {    return this  },  hide: function () {    console.log('hide')    return this  }}var jq = new Jquery()jq.css().hide()复制代码

这里jq调用了css方法,首先会在jq上找css方法,没有找到,然后就去其构造函数Jquery的原型中找,找到css方法并执行,在原型css方法里return this中的this指向的是jq

两者区别

promise执行了then或者catch方法后,每一次返回的是一个新的Promise实例,jquey一直返回的都是其本身

promise的then方法是如何确定在前面resolve或者reject执行完拿到结果再执行的

这里学习了一下别人写的promise,通过这个学习到了一些,中间关于这部分的代码

const PENDING = 'pending'const RESOLVED = 'resolved'const REJECTED = 'rejected'// promise 对象上有resolve,reject,all,race等方法,原型上有then,catch,finally方法class myPromise {  constructor (fn) {    // 首先需要判断fn是否是一个函数    if (typeof fn != 'function') return 'fn is not function'    this.PromiseStatus = PENDING    this.PromiseValue = null    this.PromiseReason = null    this.onResolvedCallbacks = []    this.onRejectedCallbacks = []    const resolve = value => { // 代码块一      if(value instanceof Promise) {        return value.then(resolve, reject);      }      // new Promise构造函数传进来的函数fn会立即执行,但是在new Promise内resolve和reject是异步操作(如何做到异步操作 =>使用setTimeout确保new Promise后的then和catch方法会在fn函数执行完才触发)      setTimeout(() => {        if (this.PromiseStatus == PENDING) {          this.PromiseStatus = RESOLVED          this.PromiseValue = value          this.onResolvedCallbacks.map(cb => {            this.PromiseValue = cb(this.PromiseValue)          })        }      })    }    const reject = value => {      setTimeout(() => {        if (this.PromiseStatus == PENDING) {          this.PromiseStatus = REJECTED          this.PromiseReason = value          this.onRejectedCallbacks.map(cb => {            this.PromiseReason = cb(this.PromiseReason)          })        }      })    }     try { // 代码块二      // 立即执行new Promise内的      fn(resolve, reject)    } catch (err) {      reject(err)    }  }  then (onResolved, onRejected) { // 代码块三    onResolved = typeof onResolved === 'function' ? onResolved : function (v) { return v}    onResolved = typeof onRejected === 'function' ? onRejected : function (v) { return v}    console.log('this.PromiseStatus', this.PromiseStatus)    if (this.PromiseStatus === RESOLVED) { // 代码块三 条件一      return new myPromise((resolve, reject) => {        // 什么时候会出现resolve一进来就变成RESOLVED或者REJECTED? 同一个Promise实例被多次调用其then时,这样会走下面的逻辑,为确定then内部还是会异步执行,使用setTimeout        setTimeout(() => {          try {            // 执行then内部的方法            let x = onResolved(this.PromiseValue)            resolvePromise(newPromise, x, resolve, reject);          } catch (err) {            reject(err)          }        })      })    }    if (this.PromiseStatus === REJECTED) { // 代码块三 条件二      return new myPromise((resolve, reject) => {        setTimeout(() => {          try {            let x = onRejected(this.PromiseReason)            resolvePromise(newPromise, x, resolve, reject);          } catch (err) {            reject(err)          }        })      })    }    // 当没有触发resolve或者reject时,将onResolved和onRejected存放在onResolvedCallbacks/onRejectedCallbacks集合中    if (this.PromiseStatus === PENDING) { // 代码块三 条件三      return new myPromise ((resolve, reject) => {        this.onResolvedCallbacks.push(value => {          try {            let x = onResolved(value)            resolvePromise(newPromise, x, resolve, reject);          } catch (err) {            reject(err)          }        })        this.onRejectedCallbacks.push(reason => {          try {            let x = onRejected(reason)            resolve(x)          } catch (err) {            reject(err)          }        })      })    }  }}复制代码

根据以上代码,我们进行简单的分析:

new myPromise(function (resolve, reject) {    console.log(1)    resolve(2)}).then(function (success) {    console.log(success)})复制代码

上面的代码中,首先构造函数初始化一些值

this.PromiseStatus = PENDING // 初始promise的状态this.PromiseValue = null // 初始promise resolve状态时的value值this.PromiseReason = null // 初始promise reject状态时的value值this.onResolvedCallbacks = [] // 存储resolved状态对应的onResolvedCallbacks事件数组this.onRejectedCallbacks = [] // 存储rejected状态对应的onRejectedCallbacks事件数组复制代码

例如上面简单的例子:其经历的步骤有:

  1. 首先进入myPromise构造函数内,进入代码块二,立即执行myPromise传进来的函数fn,这里PromiseStatus=PENDING

    try { // 代码块二    // 立即执行new Promise内的    fn(resolve, reject)} catch (err) {    reject(err)}复制代码
  2. 执行fn时,进一步执行resolve函数,进入代码块一,这里首先对resolve传进来的参数进行了类型判断,然后我们可以看到setTimeout代码,这里将setTimeout内部的事件放在eventLoop的最后。这里PromiseStatus=PENDING

    setTimeout(() => {    if (this.PromiseStatus == PENDING) {      this.PromiseStatus = RESOLVED      this.PromiseValue = value      this.onResolvedCallbacks.map(cb => {        this.PromiseValue = cb(this.PromiseValue)      })    }  })复制代码
  3. 触发myPromise的then方法,进入到代码块三,进行条件判断,进入到代码块三的 条件三,返回一个新的myPromise实例,这里会立即执行下面代码

    this.onResolvedCallbacks.push(value => {    try {        let x = onResolved(value)        resolvePromise(newPromise, x, resolve, reject);    } catch (err) {        reject(err)    }})复制代码

    这个操作就是向onResolvedCallbacks里push一个函数

  4. 上述操作结束后,再执行前面放在eventLoop最后的事件

    setTimeout(() => {    if (this.PromiseStatus == PENDING) {      this.PromiseStatus = RESOLVED      this.PromiseValue = value      this.onResolvedCallbacks.map(cb => {        this.PromiseValue = cb(this.PromiseValue)      })    }})复制代码

    这里找到更改了PromiseStatus的状态,并执行onResolvedCallbacks数组中的函数,这个函数是之前代码块三中的

    value => {    try {        let x = onResolved(value)        resolvePromise(newPromise, x, resolve, reject);    } catch (err) {        reject(err)    }}复制代码

所以,promise的then方法是如何确定在前面resolve或者reject执行完拿到结果再执行的,分两种情况:

a = new Promise((resolve, reject) => {    resolve(1)})复制代码
  • 对a只调用了一次then方法:这里进入then方法,判断状态为pending,直接返回一个new Promise(),像存放resolve状态的数组内push一个函数f。在所有同步操作完成后,在eventLoop的末尾执行resolve内部的函数,从resolve状态的数组中取出函数f并执行。
  • 对a被调用多次then方法:在第二次调用then方法时,前面的promise状态已经变为resolved或者reject,这样,进入then方法,判断状态为resolved或者reject,就setTimeout,将then内部的函数放在eventLoop的末尾。

转载于:https://juejin.im/post/5cf0d51e5188254569407029

你可能感兴趣的文章
python写的的语音天气预报
查看>>
cesium加载shp格式数据
查看>>
vue的项目结构记录
查看>>
博客已搬家,新博地址 http://www.yiven.vip
查看>>
Openstack的nova-network的vlan模式扩展
查看>>
在SContruct中编译.c
查看>>
JavaScript中点击按钮弹出新的浏览器窗口
查看>>
菜鸟Scrum敏捷实践系列(三)用户故事的组织---功能架构的规划
查看>>
让ubuntu开启ssh服务以及让vi/vim正常使用方向键与退格键
查看>>
Jmeter性能测试 入门 (Z)
查看>>
CodeForcs 1169B Good Triple
查看>>
windows环境搭建禅道项目管理工具
查看>>
Fibonacci数列
查看>>
10个带源码的充满活力的Web设计教程
查看>>
[14]CSS3 文本效果
查看>>
hdu1325 并查集
查看>>
简易时间序列分析的方法总结(R实现)
查看>>
JAVA web简单的登录界面jsp实现
查看>>
10.两个链表的交叉
查看>>
dynamic 是什么
查看>>