Promise学习笔记(尚硅谷)

一、Promise 的理解和使用

1.1 Promise 是什么?

1.1.1 理解

1.抽象表达:

        1)Promise 是一门新的技术(基于ES6规范)

        2)Promise 是 JS 中进行异步编程的新解决方案

ps:旧方案是单纯使用回调函数

2.具体表达:

        1)从语法上来说:Promise 是一个构造函数

        2)从功能上来说:Promise 对象用来封装一个异步操作并可以获取其成功/失败的结果值

1.1.2 Promise 的状态改变

pending 变为 resolved 或

              变为 rejected

ps:只有这两种状态,且一个 Promise 对象只能改变一次,无论变为成功还是失败,都有一个结果数据 => 成功称为 value , 失败称为 reason。

1.1.3 Promise 的基本流程

图源尚硅谷

1.1.4 Promise 的基本使用

1.基本代码

// 创建 promise 对象(pending 状态), 指定执行器函数
const p = new Promise((resolve,reject) =>{
    // 在执行器中启动异步任务
    setTimeout(() => {
        const time = Date.now()
        if(time % 2 === 1){
            resolve('成功的值' + time)  //若成功,调用resolve(), 指定成功的 value 并且变成 resolved 状态
        }else{
            reject('失败的值' + time)   //若失败,调用reject(), 指定成功的 reason 并且变成 rejected 状态
        }
    },1000)
})

p.then(value =>{
    console.log(value)
},reason =>{
    console.log(reason)
})

2.使用 Promise 封装基于定时器的异步

function doDelay(time) {
    // 创建 Promise 对象
    return new Promise((reslove, reject) => {
        // 启动异步任务
        console.log('启动异步任务')
        setTimeout(() => {
            console.log('延迟任务开始执行')
            const time = Date.now()
            if (time % 2 === 1) {
                resolve('成功的数据' + time)
            } else {
                reject('失败的数据' + time)
            }
        }, time);
    });
}

const Promise = doDelay(2000)
Promise.then(value => {
    console.log(value)
}, reason => {
    console.log(reason)
})

3.使用 promise 封装 ajax 异步请求

function sendAjax(url) {
    return new Promise((reslove, reject) => {
        let xhr = new XMLHttpRequest()
        xhr.open('GET', rul)
        xhr.onreadystatechange = function () {
            if (xhr.readyState === 4) {
                if (xhr.status === 200) {
                    reslove(xhr.response)
                } else {
                    reject(xhr.status)
                }
            }
        }
        xhr.send
    });
}

sendAjax('http://localhost:3000/posts')
.then(value => {
    console.log(value)
}, reason => {
    console.log(reason)
})

1.2 为什么要用 Promise ?

1.2.1 指定回调函数的方式更加灵活

1.旧方法:必须在启动异步任务之前指定

2.Promise:启动异步任务 => 返回 Promise 对象 => 给 Promise 对象绑定回调函数(一个或多个)

1.2.2 支持链式调用,可以解决 回调地狱 问题

1.什么是回调地狱 ?

        回调函数嵌套调用,外部回调函数异步执行的结果是嵌套的回调执行的条件

2.回调地狱的缺点?

        不便于阅读

        不便于异常处理

3.解决方案?

        Promise 链式调用

4.终极解决方案?

        async/await

1.3 如何使用 Promise ?

 1.3.1 API

1.Promise 构造函数:Promise(executor){}

        1)excutor 函数:执行器(resolve,reject)=> {}

        2)resolve 函数:内部定义成功时调用的函数 value => {}

        3)reject 函数:内部定义失败时调用的函数 reason => {}

ps:executor 会在 Promise 内部立即同步调用,在执行器中执行异步操作

2.Promise.prototype.then 方法:(onResolved,onRejected) => {}

        1)onResolved 函数:成功的回调函数 (value) => {}

        2)onRejected 函数:失败的回调函数 (reason) => {}

ps:指定用于得到成功 value 的成功回调和用于失败 reason 的失败回调,返回一个新的 Promise          对象

3.Promise.prototype.catch 方法:(onRejected)  => {}

        onRejected 函数:失败的回调函数 (reasone)=> {}

ps:返回一个 promise 对象,并且处理拒绝的情况。它的行为与调用                                Promise.prototype.then(undefined, onRejected) 相同

4.Promise.resolve 方法 (value) => {}

        value:成功的数据或 promise 对象

ps:返回一个成功/失败(被解析过)的 promise 对象

5.Promise.reject 方法 (reason)=> {}

        reason:失败的原因

ps:返回一个失败的 promise 对象

6.Promise.all 方法 (promises) => {}

        promises:包含n个 promise 的数组

ps:返回一个新的 promise,只有所有的 promise 都成功才成功,只要有一个失败即为失败

7.Promise.race 方法 (promises) => {}

        promises:包含n个 promise 的数组

ps:返回一个新的 promise ,第一个完成的 promise 的结果状态就是最终的结果状态

1.3.2 Promise 的几个关键问题(重点)

1.如何改变 promise 的状态?

        1)resolve(value):如果当前是 pending 就会变为 resolved

        2)reject(reason):如果当前是 pending 就会变为 rejected

        3)抛出异常:如果当前是 pending 就会变为 rejected

2.一个 promise 指定多个成功/失败回调函数都会调用吗?

        当 promise 改变为对应状态时都会调用

3.改变 promise 状态和指定回调函数的先后顺序?

        1)都有可能,正常情况下时先指定回调再改变状态,但也可以改变状态再指定回调

        2)如何先改变状态,再指定回调?

                        ①在执行器中直接调用 resolve()/ reject()

//先改状态, 后指定回调函数
new Promise((resolve, reject) => {
  resolve(1) // 先改变的状态(同时指定数据)
}).then(// 后指定回调函数, 异步执行回调函数
  value => {console.log('value2', value)},
  reason => {console.log('reason2', reason)}
)

                        ②延迟更长时间才调用 then():此时异步执行

// 常规: 先指定回调函数, 后改变的状态
new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve(1) // 后改变的状态(同时指定数据), 异步执行回调函数
  }, 1000);
}).then(// 先指定回调函数, 保存当前指定的回调函数
  value => {},
  reason => {console.log('reason', reason)}
)

        3)什么时候才能得到数据?

                ① 如果先指定的回调,那当状态发生改变时,回调函数就会调用,得到数据

                ② 如果先改变状态,则当指定回调时,回调函数被调用,得到数据

4.promise.then() 返回的新 promise 的结果状态由什么决定?

        1)简单表达:由 then()指定的回调函数执行结果决定

        2)详细表达:

                ① 如果抛出异常,新 promise 变为 rejected,reason为抛出的异常

                ② 如果返回的是非 promise 的任意值,新 promise 变为 resolved ,value 为返回的值

                ③ 如果返回的是另一个新 promise,此 promise 的结果就会成为新 promise 的结果

5.promise 中如何串联多个操作任务?

        1)promise 的 then()返回一个新的 promise ,可以变成 then()的链式调用

        2)通过 then()的链式调用串联多个同步/异步任务

6.promise 异常穿透?

        1)当使用 promise 的 then()链式调用时,可以在最后指定失败的回调

         2)前面任何操作出了异常,都会传到最后失败的回调中处理

7. 如何中断 promise 链?【面试考点

        (注意这里是中断而不是终止,因为 Promise 无法终止,这里的意思是:在合适的时候,把 pending 状态的 promise 给 reject 掉。)

        1) 当使用 promise 的 then 链式调用时, 在中间中断, 不再调用后面的回调函数

        2) 办法: 在回调函数中返回一个 pendding 状态的 promise 对象
        【原因】:因为执行then中的成功还是失败的回调,是由上一个promise的状态决定的,如果中间返回一个初始状态的promise对象,then方法不知道执行哪一个回调,后面的then方法也不会执行,promisethen链会中断。

二、手写 Promise

声明 Promise


首先,promise 是一个类,所以用class来声明

  • new Promise((resolve, reject)=>{}), 因此要传入一个参数(函数),叫做executor
  • executor 里面有两个参数,resolve 和 rejecte ,表示成功/失败
class Promise{
    // 构造器
    constructor(executor){
        // 成功
        let resolve = () => {};
        // 失败
        let reject = () => {};
        // 立即执行
        executor(resolve,reject)
    }
}

解决基本状态


官方对Promise有如下规定:

  • Promise 存在三个状态(state):pending、fulfilled、rejected;
  • pending(等待态)为初始态,并可以转化为 fulfilled(成功态)和rejected(失败态);
  • 成功时,不可转为其他状态,并且需要有一个不可改变的值(value);
  • 失败时,不可转为其他状态,并且需要有一个不可改变的原因(reason);
  • new Promise((resolve, reject)=>{resolve(value)}) resolve为成功,接收value参数,并且改变状态为fulfilled,不可以再次改变;
  • new Promise((resolve, reject)=>{reject(reason)}) reject为成功,接收reason参数,并且改变状态为rejected,不可以再次改变;
  • 若 executor 函数报错,直接执行reject();

因此得到以下代码:

class Promise {
    constructor(executor) {
        // 初始化state为等待态
        this.state = 'pending';
        //成功的值
        this.value = undefined;
        //失败的原因
        this.reason = undefined;

        let resolve = value => {
            // state 改变,resolve 调用就会失败
            if (this.state === 'pending') {
                // resolve调用后,state转化为成功态
                this.state = 'fulfilled';
                // 储存成功的值
                this.value = value;
            }
        }
        let reject = reason => {
            // state 改变,reject 调用就会失败
            if (this.state === 'pending') {
                // resolve调用后,state转化为成功态
                this.state = 'rejected';
                // 储存成功的值
                this.reason = reason;
            }
        }

        // 若 executor 执行报错,则直接调用 reject
        try{
            executor(resolve,reject);
        }catch(err){
            reject(err);
        }

    }
}

then方法


promise 有一个叫做 then 的方法,里面有两个参数:onFulfilled,onRejected,成功返回值,失败返回原因

  • 当state状态为fulfilled,则执行onFulfilled,传入this.value;
  • 当state状态为rejected,则执行onRejected,传入this.reason;
  • 因此把onFulfilled、onRejected看作函数,则必须分别在fulfilled,rejected之后被调用,value或reason依次作为他们的第一个参数
class Promse {
    constructor(executor) {... }
    // then方法,有两个参数 onFulfilled 和 onRejected
    then(onFulfilled, onRejected) {
        // 状态为fulfilled,执行onFulfilled,传入成功的值
        if (this.state === 'fulfilled') {
            onFulfilled(this.value)
        }
        // 状态为rejected,执行onRejected,传入失败的原因
        if (this.state === 'rejected') {
            onRejected(this.reason)
        }
    }
}

功能已经基本实现,但无法对付setTimeout

解决异步实现


现在可以基本实现简单的同步代码,但是当resolve在setTimeout内执行,then这时的state还是pending的状态,所以我们需要在then调用的时候,将失败和成功存到各自的数组,一旦reject或者resolve,就调用他们

类似订阅发布,先将then里面的两个函数储存起来,由于一个promise可以有多个then,所以存在同一个数组内

// 解决异步调用
class Promise {
    constructor(executor){
        this.state = 'pending';
        this.value = undefined;
        this.reason = undefined;
        // 成功存放的数组
        this.onResolvedCallbacks = [];
        // 失败存放的数组
        this.onRejectedCallbacks = [];

        let resolve = value =>{
            if(this.state === 'pending'){
                this.state = 'fulfilled'
                this.value = value
                // 一旦resolve执行,调用成功数组的函数
                this.onResolvedCallbacks.forEach(fn => fn())
            }
        }
        let reject = reson => {
            if(this.state === 'padding'){
                this.state = 'fulfilled'
                this.reason = reson 
                // 一旦reject执行,调用失败数组的函数
                this.onRejectedCallbacks.forEach(fn => fn())
            }
        }

        then(onFulfilled,onRejected){
            if(this.state === 'fulfilled'){
                onFulfilled(this.value)
            };
            if(this.state === 'rejected'){
                onRejected(this.reason)
            }
            // 当状态state为pending时
            if(this.state === 'pending'){
                // onFulfilled 传入到成功数组
                this.onResolvedCallbacks.push(() => {
                    onFulfilled(this.value)
                })
                // onRejected 传入到失败数组
                this.onRejectedCallbacks.push(() => {
                    onRejected(this.reason)
                })
            }
        }
    }
}

解决链式调用


我们常常用到 new Promise().then().then() ,这就是链式调用,解决回调地狱问题

1.为了达成链式,我们默认在第一个then里返回一个promise。官方规定了一个方法,在then里面返回一个新的promise,称为promise2:promise2 = new Promise(resolve,reject) => {}

  •  将这个promise2返回的值传递到下一个then中
  • 如果返回一个普通的值,则将普通的值传递给下一个then中

2.当我们在第一个then中 return 了一个参数(需判断)。这个return出来的新promise就是onFulfilled()或onRejected()的值

则规定onFulfilled()或onRejected()的值,即第一个then返回的值,叫做x,判断x的函数叫做resolvePromise

  • 首先,要看x是不是promise
  • 如果是promise,则取他的结果,作为新promise2成功的结果
  • 如果是普通值,直接作为promise2的成功结果
  • 所以比较x和promise2
  • resolvePromise的参数有promise2(默认返回的promise),x(我们自己return的对象),resolve,reject
  • resolve和reject是promise2 的
class Promise {
    constructor(executor) {
        this.state = 'pending';
        this.value = undefined;
        this.reason = undefined;
        // 成功存放的数组
        this.onResolvedCallbacks = [];
        // 失败存放的数组
        this.onRejectedCallbacks = [];

        let resolve = value => {
            if (this.state === 'pending') {
                this.state = 'fulfilled'
                this.value = value
                this.onResolvedCallbacks.forEach(fn => fn())
            }
        }
        let reject = reson => {
            if (this.state === 'padding') {
                this.state = 'fulfilled'
                this.reason = reson
                this.onRejectedCallbacks.forEach(fn => fn())
            }
        }

        try {
            executor(resolve, reject);
        } catch (err) {
            reject(err);
        }
    }
    then(onFulfilled, onRejected) {
        // 声明返回的promise2
        let promise2 = new Promise((reslove, reject) => {
            if (this.state === 'fulfilled') {
                let x = onFulfilled(this.value)
                // resolvePromise函数,处理自己return的promise和默认promise2的关系
                resolvePromise(promise2, x, reslove, reject)
            };
            if (this.state === 'rejected') {
                let x = onRejected(this.reason)
                resolvePromise(promise2, x, reslove, reject)
            }
            // 当状态state为pending时
            if (this.state === 'pending') {
                this.onResolvedCallbacks.push(() => {
                    let x = onFulfilled(this.value)
                    resolvePromise(promise2, x, reslove, reject)
                })
                this.onRejectedCallbacks.push(() => {
                    let x = onRejected(this.reason)
                    resolvePromise(promise2, x, reslove, reject)
                })
            }
        });
        // 返回promise,完成链式
        return promise2;
    }
}

完成resolvePromise函数


 如果x === promise2,则会造成循环引用,自己等待自己完成,则报“循环引用”错误

let p = new Promise(resolve => {
  resolve(0);
});
var p2 = p.then(data => {
  // 自己等待自己完成,无限循环
  return p2;
})

1.判断x

  • Otherwise,if x is an object or function,Let then be x.then
  • x 不能是 null
  • x 是普通函数,直接resolve(x)
  • x 是对象或者函数(包括promise),let then = x.then
  • 声明了then
  • 如果取then报错,则走reject()
  • 如果then是个函数,则用 call 执行 then,第一个参数是this,后面是成功的回调和失败的回调
  • 如果成功的回调还是 promise,就递归继续解析
  • 失败和成功只能调用一个,所以设定一个called防止多次调用
// 完成resolvePromise函数
function resolvePromise(promise2, x, resolve, reject) {
    // 循环引用报错
    if (x === promise2) {
        return reject(new TypeError('Chaining cycle detected for promise'))
    }
    // 防止多次调用
    let called;
    // x不是null,且x是对象或者函数
    if (x != null && (typeof x === 'object' || typeof x === 'function')) {
        try {
            // A+ 规定,声明then = x 的then方法
            let then = x.then;
            // 如果then是函数,则默认是promise
            if (typeof then === 'function') {
                // 就让then 执行,第一个参数是this,后面是成功回调 和 失败回调
                then.call(x, y => {
                    // 失败和成功只能调用一个
                    if (called) return;
                    called = true;
                    // resolve的结果依旧是 promise 那就继续解析
                    resolvePromise(promise2, x, resolve, reject);
                }, err => {
                    // 成功和失败只能调用一个
                    if (called) return;
                    called = true;
                    reject(err); // 失败了就失败了
                })
            } else {
                resolve(x);  // 直接成功即可
            }
        } catch (e) {
            // 也属于失败
            if (called) return;
            // 取then出错了那就不继续执行
            reject(e)
        }
    } else {
        resolve(x)
    }
}

解决其他问题

1.规定onFulfilled,onRejected都是可选参数,如果他们不是函数,必须被忽略

  • onFulfilled返回一个普通的值,成功时直接等于 value => value
  • onRejected返回一个普通的值,失败时如果直接等于 value => value,则会跑到下一个then中的onFulfilled中,所以直接扔出一个错误 reason = throw err

2.规定onFulfilled或onRejected 不能同步被调用,必须异步调用,我们用 setTimeout解决异步问题

  • 如果onFulfilled 或 onRejected 报错,则直接返回 reject()
    // 解决其他问题
    class Promise {
        constructor(executor) {
            this.state = 'pending';
            this.value = undefined;
            this.reason = undefined;
            this.onResolvedCallbacks = [];
            this.onRejectedCallbacks = [];
    
            let resolve = value => {
                if (this.state === 'pending') {
                    this.state = 'fulfilled'
                    this.value = value
                    this.onResolvedCallbacks.forEach(fn => fn())
                }
            }
            let reject = reson => {
                if (this.state === 'padding') {
                    this.state = 'fulfilled'
                    this.reason = reson
                    this.onRejectedCallbacks.forEach(fn => fn())
                }
            }
    
            try {
                executor(resolve, reject);
            } catch (err) {
                reject(err);
            }
        }
        then(onFulfilled, onRejected) {
            // onFulfilled 不是函数,就忽略onFulfilled,直接返回 value
            onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value
            // onRejected 如果不是函数,就忽略onRejected,直接扔出错误
            onRejected = typeof onRejected === 'function' ? onRejected : err => {throw err}
            let promise2 = new Promise((resolve,reject) => {
                if(this.state === 'function'){
                    // 异步
                    setTimeout(() => {
                        try{
                            let x = onFulfilled(this.value)
                            resolvePromise(promise2,x,resolve,reject)
                        }catch(e){
                            reject(e)
                        }
                    },0)
                }
                if(this.state === 'rejected'){
                    // 异步
                    setTimeout(() => {
                        // 如果报错
                        try{
                            let x = onRejected(this.reason)
                            resolvePromise(promise2,x,resolve,reject)
                        }catch(e){
                            reject(e)
                        }
                    }, 0);
                }
                if(this.state === 'pending'){
                    this.onRejectedCallbacks.push(() => {
                        // 异步
                        setTimeout(() => {
                            try{
                                let x = onFulfilled(this.value)
                                resolvePromise(promise2,x,resolve,reject)
                            }catch(e){
                                reject(e)
                            }
                        }, 0);
                    })
                    this.onRejectedCallbacks.push(() => {
                        // 异步
                        setTimeout(() => {
                            try{
                                let x = onRejected(this.reason)
                                resolvePromise(promise2,x,resolve,reject)
                            }catch(e){
                                reject(e)
                            }
                        }, 0);
                    })
                }
            })
            // 返回 promise 完成链式
            return promise2
        }
    }

大功告成

最后加上catch,resolve,reject,race,all 方法

class Promise{
  constructor(executor){
    this.state = 'pending';
    this.value = undefined;
    this.reason = undefined;
    this.onResolvedCallbacks = [];
    this.onRejectedCallbacks = [];
    let resolve = value => {
      if (this.state === 'pending') {
        this.state = 'fulfilled';
        this.value = value;
        this.onResolvedCallbacks.forEach(fn=>fn());
      }
    };
    let reject = reason => {
      if (this.state === 'pending') {
        this.state = 'rejected';
        this.reason = reason;
        this.onRejectedCallbacks.forEach(fn=>fn());
      }
    };
    try{
      executor(resolve, reject);
    } catch (err) {
      reject(err);
    }
  }
  then(onFulfilled,onRejected) {
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
    onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err };
    let promise2 = new Promise((resolve, reject) => {
      if (this.state === 'fulfilled') {
        setTimeout(() => {
          try {
            let x = onFulfilled(this.value);
            resolvePromise(promise2, x, resolve, reject);
          } catch (e) {
            reject(e);
          }
        }, 0);
      };
      if (this.state === 'rejected') {
        setTimeout(() => {
          try {
            let x = onRejected(this.reason);
            resolvePromise(promise2, x, resolve, reject);
          } catch (e) {
            reject(e);
          }
        }, 0);
      };
      if (this.state === 'pending') {
        this.onResolvedCallbacks.push(() => {
          setTimeout(() => {
            try {
              let x = onFulfilled(this.value);
              resolvePromise(promise2, x, resolve, reject);
            } catch (e) {
              reject(e);
            }
          }, 0);
        });
        this.onRejectedCallbacks.push(() => {
          setTimeout(() => {
            try {
              let x = onRejected(this.reason);
              resolvePromise(promise2, x, resolve, reject);
            } catch (e) {
              reject(e);
            }
          }, 0)
        });
      };
    });
    return promise2;
  }
  catch(fn){
    return this.then(null,fn);
  }
}
function resolvePromise(promise2, x, resolve, reject){
  if(x === promise2){
    return reject(new TypeError('Chaining cycle detected for promise'));
  }
  let called;
  if (x != null && (typeof x === 'object' || typeof x === 'function')) {
    try {
      let then = x.then;
      if (typeof then === 'function') { 
        then.call(x, y => {
          if(called)return;
          called = true;
          resolvePromise(promise2, y, resolve, reject);
        }, err => {
          if(called)return;
          called = true;
          reject(err);
        })
      } else {
        resolve(x);
      }
    } catch (e) {
      if(called)return;
      called = true;
      reject(e); 
    }
  } else {
    resolve(x);
  }
}
//resolve方法
Promise.resolve = function(val){
  return new Promise((resolve,reject)=>{
    resolve(val)
  });
}
//reject方法
Promise.reject = function(val){
  return new Promise((resolve,reject)=>{
    reject(val)
  });
}
//race方法 
Promise.race = function(promises){
  return new Promise((resolve,reject)=>{
    for(let i=0;i<promises.length;i++){
      promises[i].then(resolve,reject)
    };
  })
}
//all方法(获取所有的promise,都执行then,把结果放到数组,一起返回)
Promise.all = function(promises){
  let arr = [];
  let i = 0;
  function processData(index,data){
    arr[index] = data;
    i++;
    if(i == promises.length){
      resolve(arr);
    };
  };
  return new Promise((resolve,reject)=>{
    for(let i=0;i<promises.length;i++){
      promises[i].then(data=>{
        processData(i,data);
      },reject);
    };
  });
}

  • 2
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值