关于javascript:ES-Promise-对象手写Promise吊打面试官

2次阅读

共计 12537 个字符,预计需要花费 32 分钟才能阅读完成。

Promise 对象

  • Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更正当和更弱小。
  • 所谓Promise,简略说就是一个容器,外面保留着某个将来才会完结的事件(通常是一个异步操作)的后果。从语法上说,Promise 是一个对象,从它能够获取异步操作的音讯。Promise 提供对立的 API,各种异步操作都能够用同样的办法进行解决。
  • Promise对象有以下两个特点。

    (1)对象的状态不受外界影响。Promise对象代表一个异步操作,有三种状态:pending(进行中)、fulfilled(已胜利)和 rejected(已失败)。只有异步操作的后果,能够决定以后是哪一种状态,任何其余操作都无奈扭转这个状态。这也是Promise 这个名字的由来,它的英语意思就是“承诺”,示意其余伎俩无奈扭转。

    (2)一旦状态扭转,就不会再变,任何时候都能够失去这个后果。Promise对象的状态扭转,只有两种可能:从 pending 变为 fulfilled 和从 pending 变为rejected。只有这两种状况产生,状态就凝固了,不会再变了,会始终放弃这个后果,这时就称为 resolved(已定型)。

1、回调函数的应用例子

  • 方块的静止
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<style>
  .box {
    height: 100px;
    width: 100px;
    position: absolute;
    left: 0;
    top: 0;
    background-color: aqua;
  }
</style>
<div class="box"></div>
<body>
  <script>
    // window.getComputedStyleWindow.getComputedStyle()办法返回一个对象,该对象在利用流动样式表并解析这些值可能蕴含的任何根本计算后报告元素的所有 CSS 属性的值。let ele = document.querySelector(".box")
    let el = window.getComputedStyle(ele,null)["height"]
    function move(ele,arg,target,cb) {let start = parseInt(window.getComputedStyle(ele,null)[arg])// 开始
      let dis = (target - start) / Math.abs(target - start) // dis 大于 1 示意往右
      let speed = dis * 4; // 速度

      function fn() {let now = parseInt(window.getComputedStyle(ele,null)[arg])
        if(now == target) {cb&&cb("静止实现")
        }else {ele.style[arg] = now + speed + 'px'
          setTimeout(fn, 50)
        }
      }
      fn();}
    // 回调
    move(ele,"left",200,function(res){ // 向右静止完
      console.log(res);
      move(ele,"top",200,function(res){ // 到向下静止
        console.log(res);
        move(ele,"left",0,function(res){ // 向左静止
          console.log(res);
          move(ele,"top",0) // 向上静止
        })
      })
    })
  </script>
</body>
</html>

2、根本用法

ES6 规定,Promise对象是一个构造函数,用来生成 Promise 实例。

上面代码发明了一个 Promise 实例。

const promise = new Promise(function(resolve, reject) {
  // ... some code

  if (/* 异步操作胜利 */){resolve(value);
  } else {reject(error);
  }
});

Promise构造函数承受一个函数作为参数,该函数的两个参数别离是 resolvereject。它们是两个函数,由 JavaScript 引擎提供,不必本人部署。

resolve函数的作用是,将 Promise 对象的状态从“未实现”变为“胜利”(即从 pending 变为 resolved),在异步操作胜利时调用,并将异步操作的后果,作为参数传递进来;reject函数的作用是,将 Promise 对象的状态从“未实现”变为“失败”(即从 pending 变为 rejected),在异步操作失败时调用,并将异步操作报出的谬误,作为参数传递进来。

3、Promise.then()

Promise 实例具备 then 办法,也就是说,then办法是定义在原型对象 Promise.prototype 上的。它的作用是为 Promise 实例增加状态扭转时的回调函数。后面说过,then办法的第一个参数是 resolved 状态的回调函数,第二个参数是 rejected 状态的回调函数,它们都是可选的。

Promise实例生成当前,能够用 then 办法别离指定 resolved 状态和 rejected 状态的回调函数。

promise.then(function(value) {// success}, function(error) {// failure});
// Promise 对象三种状态 pending (进行中),flufilled(已胜利),rejected(已失败)let p = new Promise(function(resolve,reject) {resolve("success");
    reject("err");
})
console.log(p);
// then 办法 接管两个回调函数作为参数,一个是胜利时的回调 resolve, 一个是失败时的回调 reject
p.then(function(res){console.log("胜利回调",res)
},function(err){console.log("失败回调",err)
})

promise 新建后就会立刻执行。

let promise = new Promise(function(resolve, reject) {console.log('Promise');
  resolve();});

promise.then(function() {console.log('resolved.');
});

console.log('Hi!');

// Promise
// Hi!
// resolved

下面代码中,Promise 新建后立刻执行,所以首先输入的是 Promise。而后,then 办法指定的回调函数,将在以后脚本所有同步工作执行完才会执行,所以 resolved 最初输入。

  • 上面是异步加载图片的例子。
function onloadImg1() {return new Promise(function (resolve, reject) {let img = new Image();
        img.onload = function () {resolve("加载实现");
        }
        img.onerror = function () {reject("加载失败");
        }
        img.src='https://gimg2.baidu.com/image_search/src=http%3A%2F%2F1812.img.pp.sohu.com.cn%2Fimages%2Fblog%2F2009%2F11%2F18%2F18%2F8%2F125b6560a6ag214.jpg'
    })
}
onloadImg1().then(res => {console.log(res);
}, err => {console.log(err);
})
  • 方块的静止 Promise 革新
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<style>
  .box {
    height: 100px;
    width: 100px;
    position: absolute;
    left: 0;
    top: 0;
    background-color: aqua;
  }
</style>
<div class="box"></div>

<body>
  <script>
    // window.getComputedStyleWindow.getComputedStyle()办法返回一个对象,该对象在利用流动样式表并解析这些值可能蕴含的任何根本计算后报告元素的所有 CSS 属性的值。let ele = document.querySelector(".box")
    let el = window.getComputedStyle(ele, null)["height"]

    function move(ele, arg, target) {return new Promise((resolve, reject) => {let start = parseInt(window.getComputedStyle(ele, null)[arg])
        let dis = (target - start) / Math.abs(target - start)
        let speed = dis * 5;

        function fn() {let now = parseInt(window.getComputedStyle(ele, null)[arg])
          if (now == target) {// cb && cb("静止实现")
            resolve("静止实现")
          } else {ele.style[arg] = now + speed + 'px'
            setTimeout(fn, 50)
          }
        }
        fn();})
    }
    move(ele, "left", 200).then(res => {console.log(res);
      return move(ele, "top", 200)
    }).then(res => {return move(ele, "left", 0)
      console.log(res);
    }).then(res => {return move(ele, "top", 0)
      console.log(res);
    }).then(res => {console.log(res);
    })
  </script>
</body>

</html>

4、Promise.finally()

finally()办法用于指定不论 Promise 对象最初状态如何,都会执行的操作。该办法是 ES2018 引入规范的。

promise
.then(result => {···})
.catch(error => {···})
.finally(() => {···});

下面代码中,不论 promise 最初的状态,在执行完 thencatch指定的回调函数当前,都会执行 finally 办法指定的回调函数。

5、手写 Promise

5-1 Promise 的申明
  • Promise对象是一个构造函数,用来生成 Promise 实例。
  • 因为new Promise((resolve, reject)=>{}),所以传入一个参数(函数)executor,传入就执行。
  • executor 外面有两个参数,一个叫 resolve(胜利),一个叫 reject(失败)。
  • 因为 resolve 和 reject 可执行
class Promise {
    // 结构器
    constructor(executor) {
        // 胜利时回调
        let resolve = () => {}
        // 失败时回调
        let rejtect = () => {}
        // 立刻执行
        executor(resolve, reject);
    }
}
5-2 解决 Promise 的状态
  • 有三种状态: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) {
            // 初始状态
            this.state = 'pending'
            // 胜利的值
            this.value = undefined;
            // 失败起因
            this.reason = undefined
            // 胜利时回调
            let resolve = (value) => {if (this.state = 'pending') {
                    // resolve 调用后,state 转化为胜利状态
                    this.state = 'flufilled'
                    // 存储 value 的值
                    this.value = value
                }
            }
            // 失败时回调
            let rejtect = (reason) => {if (this.state = 'pending') {
                    // resolve 调用后,state 转化为失败状态
                    this.state = 'rejected'
                    // 存储 reason 的起因
                    this.reason = reason
                }
            };
            try {
                // 立刻执行
                executor(resolve, reject);
            } catch (err) {rejtect(err)
            }
        }
    }
5-3 then 办法
  • then办法的第一个参数是 onFulfilled 状态的回调函数,第二个参数是 onRejected 状态的回调函数,它们都是可选的。胜利有胜利的值,失败有失败的起因
  • 当状态 state 为 fulfilled,则执行 onFulfilled,传入 this.value。当状态 state 为 rejected,则执行 onRejected,传入 this.reason
  • onFulfilled,onRejected 如果他们是函数,则必须别离在 fulfilled,rejected 后被调用,value 或 reason 顺次作为他们的第一个参数
class Promise {
    // 结构器
    constructor(executor) {
        // 初始状态
        this.state = 'pending'
        // 胜利的值
        this.value = undefined;
        // 失败起因
        this.reason = undefined
        // 胜利时回调
        let resolve = (value) => {if (this.state = 'pending') {
                // resolve 调用后,state 转化为胜利状态
                this.state = 'flufilled'
                // 存储 value 的值
                this.value = value
            }
        }
        // 失败时回调
        let rejtect = (reason) => {if (this.state = 'pending') {
                // resolve 调用后,state 转化为失败状态
                this.state = 'rejected'
                // 存储 reason 的起因
                this.reason = reason
            }
        };
        try {
            // 立刻执行
            executor(resolve, reject);
        } catch (err) {rejtect(err)
        }
    }
    then(onFlufilled, onRejected) {if (this.state == 'fulfilled') {
            // 状态为 fulfilled,执行 onFulfilled,传入胜利的值
            onFlufilled(this.value)
        }
        if (this.state == 'rejected') {
            // 状态为 fulfilled,执行 onRejected,传入失败的值
            onRejected(this.reason)
        }
    }
}
5-4 解决异步实现

因为一个 promise 能够有多个 then,所以存在同一个数组内。

// 多个 then 的状况
let p = new Promise();
p.then();
p.then();
  • 胜利或者失败时,forEach 调用它们
class Promise {
    // 结构器
    constructor(executor) {
        // 初始状态
        this.state = 'pending'
        // 胜利的值
        this.value = undefined;
        // 失败起因
        this.reason = undefined;
        // 寄存胜利的数组
        this.onResolvedArr = [];
        // 寄存失败的数组
        this.onRejectedArr = [];
        // 胜利时回调
        let resolve = (value) => {if (this.state = 'pending') {
                // resolve 调用后,state 转化为胜利状态
                this.state = 'flufilled'
                // 存储 value 的值
                this.value = value;
                // 一旦 reslove 执行,调用胜利的数组
                this.onResolvedArr.forEach(fn => fn())
            }
        }
        // 失败时回调
        let rejtect = (reason) => {if (this.state = 'pending') {
                // resolve 调用后,state 转化为失败状态
                this.state = 'rejected'
                // 存储 reason 的起因
                this.reason = reason;
                // 一旦 reject 执行,调用失败的数组
                this.onRejectedArr.forEach(fn => fn())
            }
        };
        try {
            // 立刻执行
            executor(resolve, reject);
        } catch (err) {rejtect(err)
        }
    }
    then(onFlufilled, onRejected) {if (this.state == 'fulfilled') {
            // 状态为 fulfilled,执行 onFulfilled,传入胜利的值
            onFlufilled(this.value)
        }
        if (this.state == 'rejected') {
            // 状态为 fulfilled,执行 onRejected,传入失败的值
            onRejected(this.reason)
        }
        // 当状态 state 为 pending 时
        if (this.state == 'pending') {this.onResolvedArr.push(() => {onFlufilled(this.value)
            })
            this.onRejectedArr.push(() => {onRejected(this.reason)
            })
        }
    }
}
5-5 解决链式调用

我门经常用到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.onResolvedArr = [];
        // 寄存失败的数组
        this.onRejectedArr = [];
        // 胜利时回调
        let resolve = (value) => {if (this.state = 'pending') {
                // resolve 调用后,state 转化为胜利状态
                this.state = 'flufilled'
                // 存储 value 的值
                this.value = value;
                // 一旦 reslove 执行,调用胜利的数组
                this.onResolvedArr.forEach(fn => fn())
            }
        }
        // 失败时回调
        let rejtect = (reason) => {if (this.state = 'pending') {
                // resolve 调用后,state 转化为失败状态
                this.state = 'rejected'
                // 存储 reason 的起因
                this.reason = reason;
                // 一旦 reject 执行,调用失败的数组
                this.onRejectedArr.forEach(fn => fn())
            }
        };
        try {
            // 立刻执行
            executor(resolve, reject);
        } catch (err) {rejtect(err)
        }
    }
    then(onFlufilled, onRejected) {let promise2 = new Promise((resolve, reject) => {if (this.state == 'fulfilled') {
                // 状态为 fulfilled,执行 onFulfilled,传入胜利的值
                onFlufilled(this.value);
                // resolvePromise 函数,解决本人 return 的 promise 和默认的 promise2 的关系
                let x = onFlufilled(this.value);
                resolvePromise(promise2, x, resolve, reject)
            }
            if (this.state == 'rejected') {
                // 状态为 fulfilled,执行 onRejected,传入失败的值
                onRejected(this.reason);
                let x = onRejected(this.reason);
                resolvePromise(promise2, x, resolve, reject)
            }
            // 当状态 state 为 pending 时
            if (this.state == 'pending') {this.onResolvedArr.push(() => {onFlufilled(this.value);
                    let x = onFlufilled(this.value);
                    resolvePromise(promise2, x, resolve, reject)
                })
                this.onRejectedArr.push(() => {onRejected(this.reason);
                    let x = onRejected(this.reason);
                    resolvePromise(promise2, x, resolve, reject)
                })
            }
        })
        return promise2;
    }
}
5-6 实现 resolvePromise 函数

让不同的 promise 代码相互套用,叫做 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 2、当 x 是对象或者函数(默认 promise)
  • 申明了 then
  • 如果取 then 报错,则走 reject()
  • 如果 then 是个函数,则用 call 执行 then,第一个参数是 this,前面是胜利的回调和失败的回调
  • 如果胜利的回调还是 pormise,就递归持续解析 3、胜利和失败只能调用一个 所以设定一个 called 来避免屡次调用
function resolvePromise(promise2, x, resolve, reject){
  // 循环援用报错
  if(x === promise2){
    // reject 报错
    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, y, resolve, reject);
        }, err => {
          // 胜利和失败只能调用一个
          if (called) return;
          called = true;
          reject(err);// 失败了就失败了
        })
      } else {resolve(x); // 间接胜利即可
      }
    } catch (e) {
      // 也属于失败
      if (called) return;
      called = true;
      // 取 then 出错了那就不要在继续执行了
      reject(e); 
    }
  } else {resolve(x);
  }
}
5-7 解决其余问题

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 = 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 如果不是函数,就疏忽 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 === '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)
        });
      };
    });
    // 返回 promise,实现链式
    return promise2;
  }
}
正文完
 0