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

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;
  }
}

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理