技术笔记:Promise的原理探究及手写Promise

简介: 技术笔记:Promise的原理探究及手写Promise

前言:


不知道大家有没有一个烦恼,就是东西太多,学习//代码效果参考:http://hnjlyzjd.com/hw/wz_24719.html

完成后就会忘记了许多,在看Promise的时候,看到了Promise的各种规范,如Promise/A,Promise/B,Promise/D 以及 Promise/A 的升级版 Promise/A+,而es6使用了Promise/A+规范

目录结构


参考文献


进入正题


Promise介绍


Promise进入正题(手写实现)


基础框架搭建


Promise.prototype.then


Promise.prototype.finally


Promise.prototype.catch


Promise.all


Promise.race


Promise.allSettled


Promise.any


Promise.resolve


Promise.reject


Promise.try


总结


参考文献


阮一峰


Promise/A+:


gitPromise 地址:


进入正题


前言


promise 在应用开发和使用中非常广泛,本次主要是为了深入了解promise的工作原理,及使用流程进行手写自己的promise实现;


其中包含了promise的使用用法和原理实现,


Promise介绍


Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。


所谓Promise,简单说就是一个容器,里面保存着某个未来才//代码效果参考:http://hnjlyzjd.com/xl/wz_24717.html

会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。

特点:


对象不受外界影响,共包含三种状态pengding(进行中,初始状态),fulfilled(已成功)、reject(已失败);只有异步操作结果,才能够改变当前的状态,其他的手段无法改变


一旦状态改变,就不会在变,其中只包含两种可能的状态变化 pending->fulfilled和pending变为reject,只要有这两种情况发生,状态就会凝固,不会在发生改变了


缺点:


无法取消promise,一旦新建就会立刻执行无法中途取消,


如果不设置回调函数,内部发生错误不会反应到外部;


处于pending的状态时候,无法得到目前是哪一个阶段


Promise进入正题(手写实现)


使用方法可参照:


本次手写也是参照此用法,对输入和输出进行的控制


Promise 作为一个构造函数,其在new的时候就是立刻进行执行,根据其属性和行为去构建基础执行流程


基础框架搭建


promise/A+规范


此处重点梳理


Promies/A+规范要求:


当promise的状态是pending的时候,可能会转化到 fulfilled或者rejected状态


当promise状态是filfilled的时候


不能转化成其他的状态


必须返回一个value,并且这个value保持不变


当promise的状态是reject的时候


也无法转变成其他的状态


必须返回一个失败的原因,


定义promise的状态常量


const PENDING = 'pending';


const RESOLVED = 'fulfilled'; //成功


const REJECTED = 'rejected' //失败


创建promise基础框架


//创建Promise的基本类


class Promise {


//看这个属性 能够在原型上使用 看属性是否公用


constructor(executor) {


this.status = PENDING;


//成功的值


this.value = undefined;


//失败的原因


this.reason = undefined;


//回调函数存储器 主要解决异步处理流程


this.onReslovedCb = 【】; //成功回调


this.onRejectedCb = 【】; //失败回调


//成功函数


let resolve = (value) => {


//只有在pending的时候才可以调用


if (this.status == PENDING) {


this.value = value;


this.status = RESOLVED;


this.onReslovedCb.forEach(fn => fn())


}


}


//失败函数


let reject = (reason) => {


//只有在pending的时候才可以调用


if (this.status == PENDING) {


this.reason = reason;


this.status = REJECTED


this.onRejectedCb.forEach(fn => fn())


}


}


try {


//执行器 默认会立即执行


executor && executor(resolve, reject);


} catch (e) {


//执行的时候出现错误


reject(e)


}


}


}


Promise.prototype.then


promise.then是用来接收promise实例的执行结果,then法可以接受两个回调函数作为参数。第一个回调函数是Promise对象的状态变为resolved时调用,第二个回调函数是Promise对象的状态变为rejected时调用。其中,第二个函数是可选的,不一定要提供。这两个函数都接受Promise对象传出的值作为参数。


先看下promise/A+的规范 粗略列举了重要的内容


promise.then(onFulfilled, onRejected)


onFulfilled,onRejected是then两个参数


如果onFulfilled 不是函数,将会被直接忽略


同理 onRejected不是函数,也会被直接忽略


onFulfilled 是函数


当promise的状态是成功状态的时候,其将会被回调,返回的value会是第一个参数


不能被进行调用在其他的状态,而且只能调用一次


onRejected是函数的时候


当promise状态是失败时候被调用,失败的原因是第一个参数


不能在其他的状态下被调用,只能被调用一次


then方法能够在同一个peomise上能够被调用多毮次


如果当前promise的状态是fulfilled/onRejected,所有的then回调都必须按照他们的调用初始顺序执行


then方法 必须返回一个promise


实现代码:根据promise/A+规范


then(onfulfilled, onrejected) {


//参数是可选则的参数 需要进行判断是否存在


onfulfilled = typeof onfulfilled == 'function' ? onfulfilled : data => data


onrejected = typeof onrejected == 'function' ? onrejected : error => {


throw error


}


//里面的函数会立刻执行


let promise2 = new Promise((resolve, reject) => {


//成功的时候


if (this.status == RESOLVED) {


//定时器处理异常 为了保障promise2已经用完了


setTimeout(() => {


//try 执行函数的时候会报错 在then里面的数据


try {


//x 需要判断是否是promise和规整化


let x = onfulfilled(this.value)


resolvePromise(promise2, x, resolve, reject)


} catch (e) {


reject(e)


}


}, 0)


}


//失败的时候


if (this.status == REJECTED) {


setTimeout(() => {


try {


let x = onrejected && onrejected(this.reason)


resolvePromise(promise2, x, resolve, reject)


} catch (e) {


reject(e)


}


}, 0)


}


//如果当前是pending 表示还没返回回来


if (this.status == PENDING) {


//如果是异步 先订阅好


this.onReslovedCb.push(() => {


//todo...


setTimeout(() => {


try {


let x = onfulfilled(this.value)


resolvePromise(promise2, x, resolve, reject)


} catch (e) {


reject(e)


}


}, 0)


})


this.onRejectedCb.push(() => {


//todo...


setTimeout(() => {


try {


let x = onrejected(this.reason)


resolvePromise(promise2, x, resolve, reject)


} catch (e) {


reject(e)


}


}, 0)


})


}


})


return promise2;


}


then方法返回一个promise的函数,注意在进行promise2的创建的时候,我们在进行处理时候可能获取的到的是underfined的promise2,因此需要开辟宏任务,promise2创建完成的时候在进行调用,而在进行处理的时候,我们在onfulfilled、和onrejected得到的参数可能不同,他们收到的参数可能为几种,


.then(data=>{


return value;


},err=>{


return value


})


value为使用者输入,可能存在的值也是不确定的,因此需要进行判断,而onfulfilled、onRejected调用后的结果也是不确定的,因此需要进行类型的判断


//判断then里面的函数返回值来进行判断 x表示当前onreject


//promise都遵循的规范,因此需要进行兼容写法


function resolvePromise(promise2, x, resolve, reject) {


//判断当前的x是不是promise 是不是同一个 如果是同一个 就不要等待来了


if (promise2 === x) {


return reject(new TypeError("调用存在错误"))


}


//如果x是对象或者函数 判断数据类型


/


typeof 基本类型


constructor


instanceof 判断实例


Object.toString


*/


if (typeof x === 'object' && typeof x !== null || typeof x == 'function') {


let called; //内部测试的时候,会成功和失败都调用一下


try {


//取返回结果 then有可能通过defineProperty定义的


let then = x.then


//当前存在then方法 姑且是Promise


if (typeof then === 'function') {


//绑定this 到返回的x上,保证不用再次取then的值


then.call(x, y => {


if (called) return;


called = true; //防止多次调用成功和失败


//y可能还是promisee //采用promise的成功结果向下传递


resolvePromise(promise2, y, resolve, reject)


}, r => {


if (called) return;


called = true;


reject(r) //采用失败结果乡下传递


}) //保证再次取到then的值


} else {


//说明x就是一个普通的对象 直接成功即可


resolve(x)


}


} catch (e) {


//promise 失败 还能进行调用成功


//是一个普通的值 直接让promise2成功即可


if (called) return;


called = true;


reject(e)


}


} else {


return resolve(x)


}


}


在then方法执行完后,Promise的实例状态就会改变成resolved、或者reject,此时then方法需要兼容一异步的调用类型,因此,当进入then函数后,如果当前的promise的状态仍然是Pending,则表示当前结果还没有返回,因此需要增加onRejectedCb、onReslovedCb用来存储当前的执行函数,一旦某一个状态改变,则进行调用该存储列表中的数据,进行回调;


Promise.prototype.finally


finally()方法用于指定不管 Promise 对象最后状态如何,都会执行的操作;


finally方法的回调函数不接受任何参数,这意味着没有办法知道,前面的 Promise 状态到底是fulfilled还是rejected。这表明,finally方法里面的操作,应该是与状态无关的,不依赖于 Promise 的执行结果。


因此可以绑定此事件在当前promise实例的then方法上,在成功的时候回调传入的函数,在失败的时候也进行回调传入的参数


/


finally 函数 promise m每次执行后都会进行执行


@param { } cb


/


Promise.prototype.finally = function (cb) {


//finally 传入函数,无论成功或者失败都会执行


return this.then(data => {


//Promise.resolve 可以等待这个promise完成


return Promise.resolve(cb().then(() => data))


}, err => {


//失败的时候也执行


return Promise.reject(cb().then(() => {


throw err


}))


})


}


Promise.prototype.catch


Promise.prototype.catch()方法是.then(null, rejection)或.then(undefined, rejection)的别名,用于指定发生错误时的回调函数。


//异常处理 用于指定发生错误时的回调函数。


//promise抛出一个错误,就被catch()方法指定的回调函数捕获


Promise.prototype.catch = function (onRejected) {


return this.then(undefined, onRejected)


}


Promise.all


Promise.all可用于接收一个数组作为参数,参数可以不是数组,但是必须有Iterator接口,且返回的每个成员都是Promise的实例,他的结果是根据传入的数据进行变化的


const p = Promise.all(【p1, p2, p3】);


只有p1、p2、p3的状态都变成fulfilled,p的状态才会变成fulfilled,此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数。


只要p1、p2、p3之中有一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。


/


全部成功才能成功,一个失败才会失败


promiseList 表示当前传递的数组对象


*/


Promise.all = function (promiseList) {


return new Promise((resolve, reject) => {


let arr = 【】;


let index = 0;


//解决多个异步并发的问题


function proceessData(key, value) {


arr【key】 = value;


if (++index == promiseList.length) {


resolve(arr)


}


}


for (let i = 0; i < promiseList.length; i++) {


let current = promiseList【i】;


if (isPromise(current)) {


current.then((data) => {


proceessData(i, data)


}, (err) => {


console.log("data")


reject(err)


})


} else {


proceessData(i, current)


}


}


})


}


function isPromise(value) {


if ((typeof value === 'object' && value !== null) || typeof value === 'function') {


if (typeof value.then == 'function') {


return true


}


}


return false;


}


Promise.race


Promise.race()方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例。


const p = Promise.race(【p1, p2, p3】);


上面代码中,只要p1、p2、p3之中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给p的回调函数。


/


方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例。


@param {array} promiseList 传递的参数列表对象


*/


Promise.race = function (promiseList) {


// console.log(promiseList)


//将values中的内容包装成promise的


if (!Array.isArray(promiseList)) {


return Promise.resolve();


}


promiseList = promiseList.map(item => {


return !isPromise(item) ? Promise.resolve(item) : item;


});


// 有一个实例率先改变状态则进行操作


return new Promise((resolve, reject) => {


promiseList.forEach((pro, index) => {


pro.then(res => {


resolve(res)


}, err => {


reject(err)


})


})


})


}


Promise.allSettled<

相关文章
|
1月前
|
数据采集 前端开发
突破技术限制:使用 request-promise 库进行美团数据获取
本文展示了如何用`request-promise`爬取美团数据,重点是通过代理IP避免封禁。安装库后,配置含代理的请求选项,如`proxy`, `auth`和`headers`,并用`cheerio`解析HTML获取餐厅菜单。通过代理服务可以提高爬虫效率。
突破技术限制:使用 request-promise 库进行美团数据获取
|
8月前
|
前端开发
Promise的用法&原理&手写实现-2
Promise的用法&原理&手写实现-2
24 1
|
1月前
|
JSON 前端开发 JavaScript
【JavaScript技术专栏】JavaScript异步编程:Promise、async/await解析
【4月更文挑战第30天】JavaScript中的异步编程通过Promise和async/await来解决回调地狱问题。Promise代表可能完成或拒绝的异步操作,有pending、fulfilled和rejected三种状态。它支持链式调用和Promise.all()、Promise.race()等方法。async/await是ES8引入的语法糖,允许异步代码以同步风格编写,提高可读性和可维护性。两者结合使用能更高效地处理非阻塞操作。
|
1月前
|
存储 前端开发 安全
快速了解std::promise的工作原理和使用
快速了解std::promise的工作原理和使用
45 3
|
1月前
|
前端开发 JavaScript API
Promise.all() 的原理与实战:简化异步逻辑的不二选择
Promise.all() 的原理与实战:简化异步逻辑的不二选择
Promise.all() 的原理与实战:简化异步逻辑的不二选择
|
1月前
|
前端开发 C++
C++11实用技术(三)std::future、std::promise、std::packaged_task、async
C++11实用技术(三)std::future、std::promise、std::packaged_task、async
45 0
|
8月前
|
前端开发 JavaScript API
Promise的用法&原理&手写实现-1
Promise的用法&原理&手写实现-1
31 0
|
11月前
|
Web App开发 JSON JavaScript
前端技术ES6新特性解构字符串扩展表达式箭头函数对象拓展运算符map 和 reduce Promise 模块化export import及Node.js
ECMAScript 6.0(以下简称 ES6,ECMAScript 是一种由 Ecma 国际(前身为欧洲计算机制造商协会,英文名称是 European Computer Manufacturers Association)通过 ECMA-262标准化的脚本程序设计语言)是 JavaScript 语言的下一代标准,已经在 2015 年 6 月正式发布了,并且从 ECMAScript 6 开始,开始采用年号来做版本。即 ECMAScript 2015,就是 ECMAScript6。它的目标,是使得 JavaScript 语言可以用来编写复杂的大型应用程序,成为企业级开发语言。
61 0
|
存储 缓存 自然语言处理
吊打面试官:promise原理详解
吊打面试官:promise原理详解
166 0
|
前端开发
重新手写promise,理解核心的异步链式调用原理
重新手写promise,理解核心的异步链式调用原理
150 0