Promsie学习笔记

70 阅读8分钟

promise 使用的原因?

JavaScript是一门单线程语言,解决异步场景大部分使用回调函数来进行,

var asuncFunc = function(cb) {
  setTimeout(function() {
    cb()
  }, 1000)
}
//多个异步函数嵌套
setTimeout(function() {
    consolelog("1")
    setTimeout(function() {
      console.log("2")
    }, 1000)
  }, 1000)

多个回调嵌套,很容易让代码变宽,为了更优雅的调用回调函数,ES6产生了一个新规范Promise, 他让异步操作的变得近乎同步化。

Promise基础

在支持ES6的高级浏览器环境中,我们通过 new primise() 即可创造一个promise实例

这个构造函数接受一个函数,分别接受两个参数,resolve和reject,代表着我们需要改变当前实例的状态到已完成 或者是已拒绝

const promise1 = function() {
  return new Promise(function(resolve, reject) {
    //异步内容
    setTimeout(function() {
      //输出完成后,调用函数传入的resolve函数,将该promise实例标记为已完成,当前promise串行继续
      resolve()
    }, 1000)
  })
}
const promise2 = function() {
  return new Promise(function(resolve, reject){
    setTimeout(function() {
      resolve();
    }, 2000)
  }) 
}

串行两个promise实例,

promise1().then(function() { return promise2()})

或者是

promise1().then(function() {promise2})

promise1在执行后,状态从pending变成了fulfilled,执行了resolve,那么就会去执行then的方法,然后执行promise2()

如果我promise从pending变成了拒绝状态,执行reject方法,会进入到catch中

const promise3 = function() {
  return new Promise(function(resolve, reject) {
    var random = Math.random() * 10;
    setTimeout(function() {
      if(random >= 5) {
        resolve(random)
      }else {
        reject(random)   
      }
    }, 1000)
  }) 
}

const onResolve = function(val) {
  console.log('已完成' + val)
}
const onReject = function(val) {
  console.log('已拒绝' + val)
}

//1.promise的then可以接受两个函数,第一个参数在resolve后执行,第二个参数在reject后执行

promise3.then(onResolve, onReject);

//2.catch拦截已拒绝状态

promise3.catch(onResolve).then(onReject)

//3.try catch

try {
  promise3().then(onResolve)
}catch {
  onReject()
}

总结

  1. promise有三个状态,pending 、fulfilled 、rejected,进行中的状态可以变更为fulfilled和rejected,已经变更过的状态无法继续变更。
  2. ES6中的Promise构造函数,构造之后传入一个函数,接受两个函数参数,执行第一个参数之后就会改变当前promise为fulfilled,执行第二个参数之后就会变为rejected状态。 3.通过.then方法,即可在上一个promise达到已完成时继续执行下一个函数或者promise。同时通过resolve或者reject时传入参数,即可给下一个函数或者promise传入初始值。 4.已拒绝的promise,后续可以通过.catch方法或是.then方法的第二个参数或是try catch进行捕获

封装异步操作为promise

//传统异步回调
function asyncFunc(cb) {
  setTimeout(function() {
    cb()
  }, 1000)
}

var callback = function() {
  console.log("2")
}

asyncFunc(callback)
//promise封装
const dynamicFuncAsync = function () {
  return new Promise(function(resolve, reject) {
    setTimeout(function() {
      resolve()
    }, 1000)
  })
}
const onResolve = function() {
  console.log("2")
}
dynamicFuncAsync().then(onResolve)

ajax请求封装

function ajax(url, success, fail) {
  var client = new XMLHttpRequest()
  client.open('GET', url)
  client.onreadystateChange = function() {
    if(this.readyState !== 4) {
      return;
    }
    if(this.status === 200) {
      success(this.response)
    }else {
      fail(new Error(this.statusText))
    }
  } 
  client.send();
}
ajax('/ajax.json', function(){console.log('成功')」, function() { console.log('失败')})
const ajaxAsync = function(url) {
  return new Promise(function(resolve, reject) {
    var client = new XMLHttoRequest()
    client.open('GET', url)
    client.onreadystatechange = function() {
      if(this.readyState !== 4) {
        return;
      }
      if(this.status === 200) {
        resolve(this.response)
      }else {
        reject(new Error(this.statusText))
      }
    }
    client.send()
  })
}
ajax('./ajax.json')
.catch(function() {
  console.log('失败')

})
.then(function() {
  console.log('成功')
})

*总结

  1. 可以把任何一个函数或者是异步函数改为promise,尤其是异步函数,改为promise之后即可进行链式调用,增强可读性。
  2. 将带有回调函数的异步改为promise,只需要在内部实例化promise之后,在原来执行回调函数的地方执行对应的更改promise状态的函数即可

promise规范解读

任何符合promise规范的对象或函数都可以成为promise。 promiseA plus 规范地址:

术语

Promise: promise是一个拥有then方法的对象或函数,其行为符合本规范。 具有then方法(thenable):是一个定义了then方法的对象或函数。 值(value):指任何JavaScript的合法指(包括 undefined,thenable 和promise)。 异常(exception):是使用throw语句抛出的一个值。 原因(reason):表示一个promise的拒绝原因。

要求

promise的状态

一个Promise的当前状态必须为以下三种状态中的一种: 等待态(Pending)、已完成(FulFilled)和拒绝(Rejected)。

  • 处于等待态时,promise需满足以下条件:可以变为 已完成已拒绝
  • 处于已完成时,promise需满足以下条件: 1.不能迁移至其他任何状态, 2.必须拥有一个不可变的值
  • 处于已拒绝时,promise需满足以下条件:1.不能迁移至其他任何状态,2.必须拥有一个不可变的原因

###必须有一个then方法 一个promise必须提供一个then方法以访问其当前值和原因。 promise的then方法接受两个参数:promise.then(onFullfiled, onRejected) 他们都是可选参数,同时他们都是函数,如果onFullfilled或onRejected不是函数,则需要忽略他们

  • 如果onFullfiled是一个函数

    • promise执行结束后其必须被调用,其第一个参数为promise的结果
    • promise执行结束前,其不可被调用
    • 其调用次数不可超过一次
  • 如果onRejected是一个函数

    • promise被拒绝执行后其必须被调用,其第一个参数为promsie的结果
    • promise被拒绝执行前其不可被调用
    • 其调用次数不可超过一次
  • 在执行上下文堆栈仅包含平台代码之前,不可调用onFulfiledonRejected

  • onFulfilledonRejected必须被作为普通函数调用(即费实例化调用,这样函数内部this非严格模式下指向window)

  • then方法可以被同一个promise调用多次

    • promise成功执行时,所有onFulfilled需按照其注册顺序依次回调
    • pormise被拒绝执行时,所有的onRejected需按照其注册顺序依次回调
  • then方法必须返回一个promise对象 promsie2 = pormise1.then(onFulfilled, onRejected);

    • 只要onFulfilled或者onRejected返回一个值x,promise2都会进入onFulfilled状态
    • 如果onFulfilled或者onRejected返回一个异常e,则 promise2必须拒绝执行,并返回拒因e
    var promise1 = new Promise(function(resolve, reject) {reject(new Error('fail'))})
    promise1
    .catch( e => {
        console.log(e + '已拒绝') //Error: fail 已拒绝
    })
    
    • 如果onFulfilled不是函数且promise1状态变为已完成, promise2必须成功执行并返回相同的值
    • 如果onRejected不是函数且promise1状态变为已拒绝, promise2必须执行拒绝回调并返回相同拒因
      var promise1 = new Promise(function(resolve, reject) {reject()})
    promise1
    .then(null, function() {
        return 123
    })
    .then(null, null)
    .then(null, null)
    .then(
        res1 => {
            console.log(res1 + '已完成') //res1已完成
        },
        e => {
            console.log(e + '已拒绝')
        }
    )
    

promise的解决过程

Promise解决过程是一个抽象的操作,其需输入一个promise和一个值,我们表示为[[Resolve]](promise, x) (这句话的意思就是把promise resolve了,同时传入x作为值)

promise.then(function(x) {
  console.log('会执行这个函数,同时传入x变量的值',x)
})

如果xthen方法且看上去像一个Promise,解决程序即尝试使promise接受x的状态;否则其用x的值来执行promise

  • 如果promisex指向同一对象,以TypeError为拒因拒绝执行promise
  • 如果x 为promsie
    • 如果x处于等待态,promise需保持为等待态直至x被执行或拒绝
    • 如果x处于执行态,用相同的值执行promise
    • 如果x处于拒绝态,用相同的拒因拒绝promise

模拟部分promise源码

//合法的promise构造函数
class CustomPromise {
    //构造函数
    //handleFunc 每个函数都会有一个回调函数传进去,对应的回调函数有两个参数
    constructor(handleFunc) {
        //最小实现
        //根据规范定义的东西
        //规范1.3,1.4,1.5
        this.state = 'pending'
        this.value = undefined

        this.fulfilledList = [];
        //执行handleFunc,里面有两个参数
        //绑定this,保证resolve方法能够传进去,this保有当前的上下文
        handleFunc(this.triggerResolve.bind(this))

    }
     
    //就是传进去的resolve,reject方法,
    triggerResolve(val) {
        //为什么是异步,setTimeout,如果直接执行resolve函数,那么就会立即的执行.then ,打印出hello world,
        //但是此时.then方法还没有注册
        //在这时需要我们将所有的.then都收集起来,这样在执行triggerResolve的哪个.then
        //2.2.4 在执行当前的上下文堆栈仅包含平台代码之前,不得调用onFulfilled 或者 onRejected
        //平台代码:
        //resolve调用在前,但是要将他放在队列之后
        setTimeout(() => {
          //已经进入了已完成的状态
          if(this.state !== 'pending') return;
          
          this.state = 'fulfilled'
          this.value = val;
          this.fulfilledList.forEach(item => item(val))
          this.fulfilledList = []
        }, 0)
    }
    //then方法收集依赖,回调函数
    then(onFulfilled, onRejected) {
      const { value, status } = this 
      //实现串联,返回一个新的promise

      const promiseInstance = new CustomPromise((onNextFulfilled, onNextRejected) => {
        //onFulfilled和onNextFulfilled联合起来
        function onFinalFulfilled(val) {
            if(typeof onFulfilled !== 'function') {
                onNextFulfilled(val)
            }else {
                const res = onFulfilled(val)
                if(typeof res.then === 'function') {
                    //res是一个promsie
                    res.then(onNextFulfilled)
                }else {
                    onNextFulfilled(res);
                }
            }
        }
        switch(status) {
            case 'pending': {
                //第一次收集
                this.fulfilledList.push(onFinalFulfilled);
                break;
            }
            
        }
      });
      return promiseInstance;
    }
    catch(onRejected) {
      return this.then(null, onRejected);
    }
    static all(list) {
        return new CustomPromise((resolve, reject) => {
            let count = 0;
            const values = [];
            for(const [i, CustomPromiseInstance] of list.entries()) {
                CustomPromiseInstance
                .then(
                    res => {
                        values[i] = res;
                        count++;
                        if(count === list.length) resolve(values);
                    },
                    err => {
                        reject(err);
                    }
                )
            }
        })
    }
    

}
//实例化一个promise
//根据特征判断,不能根据具体属性(instance)
// const  promise = new CustomPromise(function(resolve) {
//     resolve()
// })


//应用
const promise = function(time) {
    return new Promise(function(resolve, reject) {
        return setTimeout(resolve, time)
    })
}

const promiseInstance = promise(1000)
promiseInstance.then(function() {
    console.log('hello world')
})