天天造轮子第八天 - Promise - 网友来稿

简介: 天天造轮子第八天 - Promise - 网友来稿

今天发一个网友来稿


网络异常,图片无法展示
|


欢迎大家PR


方法请参考以下文章:


如何参加开源项目-如何给Vue3.0提PRjuejin.cn/post/684490…


造轮子计划


计划天天变


  • 框架








  • 原生Ajax


  • JS基础



  •  Promise


  •  Promise.all/race


  •  路由


  •  new


  •  call/apply/bind


  •  Object.create


  •  深拷贝、浅拷贝


  • 算法、设计模式


  •  二分查找


  •  快排


  •  二分查找


  •  冒泡排序


  •  选择排序


  •  订阅发布


  •  斐波那契算法


  •  去重


Promise


Promise是异步编程的一种解决方案: 从语法上讲,promise是一个对象,从它可以获取异步操作的的消息;从本意上讲,它是承诺,承诺它过一段时间会你一个结果。promise有三种状态:pending(等待态),fulfilled(成功态), rejected(失败态);状态一旦改变,就一会再变。创建promise实例后,会立即执行。Promise主要用来解决地狱回调;Promise支持多个并发请求,并获取并发请求的数据。


需求


调用Promise返回一个promise对象


基本功能Promise返回成功测试用例


it('基本功能Promise返回成功测试', (done) => {
  const promise = new BasePromise((resolve, reject) => {
    setTimeout(() => {
      resolve('success');
    }, 50);
  })
  promise.then(data => {    
    expect(data).toBe('success');
    done();
  })
})


基本功能Promise返回失败测试用例


it('基本功能Promise返回失败测试', (done) => {
  const promise = new BasePromise((resolve, reject) => {
    setTimeout(() => {
      reject('fail');
    }, 50);
  })
  promise.then(data => {    
    console.log('resolve data: ', data)
  }, error => {
    expect(error).toBe('fail');
    done();
  })
})


完整版Promise成功返回测试用例


it('完整版Promise成功返回测试', (done) => {
  const promise = new FullPromise((resolve, reject) => {
    resolve('success');
  });
  promise.then().then().then(data => {
    expect(data).toBe('success');
    done();
  })
})


完整版Promise失败返回测试用例


it('完整版Promise失败返回测试', (done) => {
  const promise = new FullPromise((resolve, reject) => {
    reject('fail');
  });
  promise.then().then().then(data => {
    console.log('resolve data: ', data);
  }, err => {
    expect(err).toBe('fail');
    done();
  })
})


功能实现


基础版Promsie


使用方法


  • 首先我们在调用Promise时,会返回一个Promise对象。


  • 构建Promise对象时,需要传入一个executor函数,Promise的主要业务流程都在executor函数中执行。


  • 如果运行在excutor函数中的业务执行成功了,会调用resolve函数;如果执行失败了,则调用reject函数。


  • Promise的状态不可逆,同时调用resolve函数和reject函数,默认会采取第一次调用的结果。


根据Promise的一些主要的使用方法,结合Promise/A+规范,我们可以分析出Promise的基本特征:


  1. promise有三个状态:pending,fulfilled,rejected。


  1. new Promise时,需要传递一个executor()执行器,执行器立即执行。


  1. executor接受两个参数,分别是resolve和reject。


  1. promise的默认状态是pending。


  1. promise有一个value保存成功状态的值,可以是undefined/thenable/promise。


  1. promise有一个reason保存失败状态的值。


  1. promise只能从pending到rejected, 或者从pending到fulfilled,状态一旦确认,就不会再改变。


  1. promise 必须有一个then方法,then接收两个参数,分别是promise成功的回调onFulfilled, 和promise失败的回调onRejected。


  1. 如果调用then时,promise已经成功,则执行onFulfilled,参数是promise的value。


  1. 如果调用then时,promise已经失败,那么执行onRejected, 参数是promise的reason。


  1. 如果then中抛出了异常,那么就会把这个异常作为参数,传递给下一个then的失败的回调onRejected。


源码:


// promise 的状态值
const PENDING = 'PENDING',
      FULFILLED = 'FULFILLED',
      REJECTED = 'REJECTED';
/*
* 基础版Promise,提供最基础的功能
*/
class BasePromise {
  constructor( executor ) {
    this.status = PENDING;              // 默认状态为PENDING
    this.value = undefined;             // 保存成功状态的值,默认为undefined
    this.reason = undefined;            // 保存失败状态的值
    this.onResolvedCallbacks = [];      // 保存成功的回调
    this.onRejectedCallbacks = [];      // 保存失败的回调
    // 成功时调用的方法
    const resolve = ( value ) => {
      // 状态为PENDING时才可以更新状态,防止executor中调用两次resolve/reject方法
      if ( this.status === PENDING ) {
        this.status = FULFILLED;
        this.value = value;
        // 依次执行对应的函数
        this.onResolvedCallbacks.forEach(fn => fn());
      }
    }
    // 失败时调用的方法
    const reject = ( reason ) => {
      // 状态为PENDING时才可以更新状态,防止executor中调用两次resolve/reject方法
      if ( this.status === PENDING ) {
        this.status = REJECTED;
        this.reason = reason;
        this.onRejectedCallbacks.forEach(fn => fn());
      }
    }
    try {
      // 立即执行, 将resolve和reject函数传给使用者
      executor( resolve, reject);
    }
    catch ( error ) {
      // 发生异常是执行逻辑
      reject( error )
    }
  }
  // 包含一个then方法,并接收两个参数onFulfilled, onRejected
  then( onFulfilled, onRejected ) {
    if ( this.status === FULFILLED ) {
      onFulfilled(this.value);
    }
    else if ( this.status === REJECTED ){
      onRejected(this.reason);
    }
    else if ( this.status === PENDING ) {
      // 如果promise的状态是PENDING,需要将onFulfilled和onRejected函数存放起来,等待状态确定后,再依次执行对应的函数
      this.onResolvedCallbacks.push(() => { onFulfilled(this.value); });
      this.onRejectedCallbacks.push(() => { onRejected(this.reason); });
    }
  }
}


完整版Promsie, 会继承基础版Promise类


提供链式调用与值穿透特性: 链式调用,我们使用Promise时候,当then函数中return一个值时,不管是什么值,我们都能在下一个then中获取到,这就是then的链式调用; 值穿透特性,当我们不在then中放入参数,如: promise.then().then(),那么后面的then

依旧可以得到之前then返回的值,这就是所谓的值的穿透。


实现原理: 如果每次调用then的时候,我们都重新创建一个promise对象,并把上一个then的返回结果传给这个新的promise的then方法,这样就可以一直then下去。


源码


function resolvePromise( promise2, x, resolve, reject ) {
  // 如果自己等待自己完成则是错误的实现,用一个类型错误,结束掉promise
  if ( promise2 === x ) {
    return reject( new TypeError('Chaining cycle detected for promise #<Promise>') );
  }
  let called;                              // 是否已调用,只调用一次
  if ( (typeof x === 'object' && x !== null) || typeof x === 'function' ) {
    try {
      // 为了判断resolve过就不再reject了(如reject与resolve同时调用的时候)
      let then = x.then;
      if ( typeof then === 'function') {
        // 不要写成x.then,直接then.call就可以了, 因为x.then会再次取值。
        then.call(x, y => {
          // 如果执行过,则不再执行
          if ( called ) { return; }
          called = true;
          // 递归解析(因为可能promise中还有promise)
          resolvePromise(promise2, y, resolve, reject);
        }, r => {
          // 只要失败就reject
          if ( called ) { return; }
          called = true;
          reject(r)
        })
      }
      else {
        // 如果x.then是一个普通值就直接返回resolve作为结果
        resolve(x);
      }
    }
    catch ( error ) {
      if ( called ) { return; }
      called = true;
      reject( error );
    }
  }
  else {
    // 如果x.then是一个普通值就直接返回resolve作为结果
    resolve(x);
  }
}
/*
* Promise完整版类
*/
class FullPromise extends BasePromise {
  constructor( executor ) {
    super( executor )
  }
  // 重写基类的方法
  then( onFulfilled, onRejected ) {
    // 解决onFulfilled, onRejected没有传值的问题
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : v => v;
    // 因为错误的值要让后面访问到,所以这里也要跑出个错误,不然会在之后then的resolve中捕获
    onRejected = typeof onRejected === 'function' ? onRejected : error => { throw error; };
    // 每次调用then都返回一个新的promise
    const promise2 = new Promise((resolve, reject) => {
      if ( this.status === FULFILLED ) {
        setTimeout(() => {
          try {
            const x = onFulfilled( this.value );
            // x可能是一个promise
            console.log('fulfilled', promise2);
            resolvePromise(promise2, x, resolve, reject);
          }
          catch ( error ) {
            reject( error );
          }
        }, 0)
      }
      if ( this.status === REJECTED ) {
        setTimeout(() => {
          try {
            const x = onRejected( this.reason );
            resolvePromise(promise2, x, resolve, reject);
          }
          catch ( error ) {
            reject( error )
          }
        }, 0)
      }
      if ( this.status === PENDING ) {
        this.onResolvedCallbacks.push(() => { 
          try {
            const x = onFulfilled( this.value );
            resolvePromise( promise2, x, resolve, reject );
          }
          catch ( error ) {
            reject( error );
          }
        });
        this.onRejectedCallbacks.push(() => { 
          try {
            const x = onRejected( this.reason );
            resolvePromise( promise2, x, resolve, reject );
          }
          catch ( error ) {
            reject( error );
          } 
        });
      }
    })
    return promise2;
  }
}


测试


网络异常,图片无法展示
|


OK 任务完成


相关文章
|
6月前
|
前端开发 JavaScript
如何处理 JavaScript 中的异步操作和 Promise?
如何处理 JavaScript 中的异步操作和 Promise?
65 1
|
6月前
|
前端开发 JavaScript
在JavaScript中,什么是promise、怎么使用promise、怎么手写promise
在JavaScript中,什么是promise、怎么使用promise、怎么手写promise
99 4
|
6月前
|
前端开发 JavaScript 开发者
JavaScript 中的异步编程:Promise 和 Async/Await
在现代的 JavaScript 开发中,异步编程是至关重要的。本文将介绍 JavaScript 中的异步编程概念,重点讨论 Promise 和 Async/Await 这两种常见的处理异步操作的方法。通过本文的阐述,读者将能够更好地理解和应用这些技术,提高自己在 JavaScript 开发中处理异步任务的能力。
|
5月前
|
前端开发 JavaScript 开发者
JavaScript进阶-Promise与异步编程
【6月更文挑战第20天】JavaScript的Promise简化了异步操作,从ES6开始成为标准。Promise有三种状态:pending、fulfilled和rejected。基本用法涉及构造函数和`.then`处理结果,如: ```javascript new Promise((resolve, reject) =&gt; { setTimeout(resolve, 2000, &#39;成功&#39;); }).then(console.log); // 输出: 成功
86 4
|
6月前
|
JSON 前端开发 JavaScript
【JavaScript技术专栏】JavaScript异步编程:Promise、async/await解析
【4月更文挑战第30天】JavaScript中的异步编程通过Promise和async/await来解决回调地狱问题。Promise代表可能完成或拒绝的异步操作,有pending、fulfilled和rejected三种状态。它支持链式调用和Promise.all()、Promise.race()等方法。async/await是ES8引入的语法糖,允许异步代码以同步风格编写,提高可读性和可维护性。两者结合使用能更高效地处理非阻塞操作。
89 0
|
4月前
|
前端开发 JavaScript
JavaScript异步编程:Promise与async/await的深入探索
【7月更文挑战第9天】Promise和async/await是JavaScript中处理异步编程的两大利器。Promise为异步操作提供了统一的接口和链式调用的能力,而async/await则在此基础上进一步简化了异步代码的书写和阅读。掌握它们,将使我们能够更加高效地编写出清晰、健壮的异步JavaScript代码。
|
4月前
|
前端开发 JavaScript 定位技术
JavaScript 等待异步请求数据返回值后,继续执行代码 —— async await Promise的使用方法
JavaScript 等待异步请求数据返回值后,继续执行代码 —— async await Promise的使用方法
55 1
|
5月前
|
前端开发 JavaScript
Promise是JavaScript解决异步问题的构造器,代表未来的不确定值。
【6月更文挑战第27天】Promise是JavaScript解决异步问题的构造器,代表未来的不确定值。它避免了回调地狱,通过链式调用`.then()`和`.catch()`使异步流程清晰。
48 2
|
5月前
|
前端开发 JavaScript
JavaScript异步处理避免了单线程阻塞,如回调函数、Promise和async/await。
【6月更文挑战第22天】JavaScript异步处理避免了单线程阻塞,如回调函数、Promise和async/await。回调是基础,用于在操作完成后执行函数;Promise管理异步状态,支持链式调用;async/await提供同步代码外观,简化错误处理。每种技术在处理耗时任务时都起着关键作用。
48 3

热门文章

最新文章