《JavaScript》异步编程(三)深入浅出Promise(3)

《JavaScript》异步编程(三)深入浅出Promise(3)then

大家好,欢迎来到IT知识分享网。

该示例代码中的getData就是Promise实现的,并且,当出现多个的时候,只要返回的是Promise,那么就可以一直.then()链式调用下去,别的不说,地狱回调的问题就已经解决了。

回到上面提出的问题,Promise的出现就是为了提升了JS的异步能力,成为了前端主流的异步编程方案,因此它成为了很多前端框架的宠儿,或者说很多前端框架是基于它实现的,举个例子:axios,它就是基于Promise实现的;

规范

============================================================

在介绍什么是Promise之前,首先明确,Promise也是有规范的,ES6d的Promise就是基于这个规范实现的,这个规范就是Promise/A+,简单说下这个规范,通过这个规范,我们可以更好的理解Promise,规范由3个部分组成:

术语


在这个部分,规范一共定义了5个名词:

  • Promise:一个拥有符合这个规范的行为的then方法的对象或函数;
  • thenable:定义了一个then方法的对象或函数;
  • 值(value):任意合法的JavaScript值(包括undefined,thenable,promise);
  • 异常(exception):使用throw语句抛出的一个值;
  • 原因(reason):表示promise为什么被拒绝的一个值;

必要条件


在这个部分,规范就主要制定了Promise的条件,比如Promise的状态,又比如必须包含then方法,这边就不做赘述了,具体可以看一下这篇文章《[Promises/A+规范]( )》

注解


这部分就简单了,简单讲了一下主要注意的地方,或者解释的东西;

简介

============================================================

接着,我们来看下什么是Promise?

官方的描述:Promise 对象用于表示一个异步操作的最终完成 (或失败)及其结果值。一个 Promise 对象代表一个在这个 promise 被创建出来时不一定已知的值。它让您能够把异步操作最终的成功返回值或者失败原因和相应的处理程序关联起来。 这样使得异步方法可以像同步方法那样返回值:异步方法并不会立即返回最终的值,而是会返回一个 promise,以便在未来某个时候把值交给使用者;

个人理解,就是定义了一个异步操作,并且在未来的某个时候,这个Promise会通过改变其自身的状态,来告诉后面的程序,可以执行接下来的代码了,我作为一个异步已经结束了;

具体看下Mozilla的示例图:

在这里插入图片描述

解释一下,Promise一共有三种状态:待定(pending)已兑现(fulfilled)已拒绝(rejected),看代码解释:

new Promise((resolve,reject)=>{

// to do something

if(…){

resolve()

}

else{

reject()

}

})

当代码在执行的时候,也就是处于to do something的时候,Promise的是处于pending状态的,而一旦执行了resolve那么Promise就会处于fulfilled状态,如果在pending的时候,执行reject(),那么Promise就变成rejected状态,状态一旦变更成fulfilled或者rejected,那么Promise就不会再回到pending了;

好吧,说到这里,可能会问,这些讲的都太抽象了,根本看不懂,好吧,确实,那么我们直接在浏览器打印一下Promise

在这里插入图片描述

从结果来看,很明显,这是一个构造函数,常用的catch,then这些方法都是挂载其prototype上,既然是构造函数,那么不就随便new了嘛

const promise = new Promise()

因为是new出来的自然而然继承了其原型上的方法,那么下面这种个方法自然就可以使用了

promise.then()

promise.catch()

Promise接收一个函数作为参数,并且这个函数有两个参数,这两个参数也都是函数

const promise = new Promise((resolve,reject)=>{

// …

})

resolve代表将Promise的状态从pending改变成fulfilled,而如果reject代表将Promise的状态从pending改为reject,如

new Promise((resolve,reject)=>{

// to do something

if(…){

resolve()

}

else{

reject()

}

})

而通过打印,我们知道,Promise还有一个then方法和catch方法,那么resolve里面的值会被作为参数传递到then的第一个参数里,reject里面的值会被作为参数传递到then的第二个参数或者catch里,如

new Promise((resolve,reject)=>{

// to do something

if(true){

resolve(1)

}

else{

reject(2)

}

}).then((res,error)=>{

console.log(res,error) // res为1,如果上面的if的条件时false,那么error的值就是2

})

而如果是失败调用catch自然也是和then一样

new Promise((resolve,reject)=>{

// to do something

if(true){

resolve(1)

}

else{

reject(2)

}

}).then((res,error)=>{

console.log(res,error) // res为1,如果上面的if的条件时false,那么error的值就是2

}).catch(error=>{

// …错误

})

到这里,可能有小伙伴会问,上面不是说Promise可以解决地狱回调的问题吗,怎么解决,地狱回调最根本的原因在于嵌套,N层嵌套使得代码的可读性,可维护性非常差,而Promise给出的方法就是通过不断的.then解决这个问题,比如

new Promise((resolve) => {

console.log(1)

resolve(2)

}).then(res => {

console.log(res)

return 3

}).then(res => {

console.log(res);

return 4

}).then(res => {

console.log(res)

return 5

}).then(res => {

console.log(res)

})

.then的返回依然是一个Promise,因此可以不断的.then()下去,但是,如果真的直接return会发现,不对啊,这个怎么是同步的,在.then里面加异步后没有按照期望的等待接口返回了在return回去,比如下例,只有1,2是正常打印的,后门的全是undefined,怎么回事

new Promise((resolve) => {

console.log(1)

resolve(2)

}).then(res => {

console.log(res)

setTimeout(() => {

return res+1

}, 1000)

}).then(res => {

console.log(res);

setTimeout(() => {

return res+1

}, 1000)

}).then(res => {

console.log(res)

setTimeout(() => {

return res+1

}, 1000)

}).then(res => {

console.log(res)

})

原因是.then虽然返回的是promise但是它是同步的,并不会等你的异步代码执行完毕后再执行,那么如果.then都是异步代码怎么办,答案很简单,new 一个promise呗

new Promise((resolve) => {

console.log(1)

setTimeout(() => {

resolve(2)

}, 1000)

}).then(res => {

console.log(res)

return new Promise(resolve => {

setTimeout(() => {

resolve(res + 1)

}, 1000)

})

}).then(res => {

console.log(res);

return new Promise(resolve => {

setTimeout(() => {

resolve(res + 1)

}, 1000)

})

}).then(res => {

console.log(res)

return new Promise(resolve => {

setTimeout(() => {

resolve(res + 1)

}, 1000)

})

}).then(res => {

console.log(res)

})

API

=============================================================

静态方法


Promise.resolve(param)

这个方法等同于下方代码,实际上就是在做一个resolve的操作

new Promise((resolve,reject)=>{

resolve(param)

})

Promise.reject(param)

同样,这个方法等同于下方代码,实际上在做一个reject的操作

new Promise((resolve,reject)=>{

reject(param)

})

Promise.all([p1,p2…,pn])

这个方法就比较特殊了,它接收一个数组作为参数,数组的每一项都是一个Promise,只当数组的每一项Promise的状态都变成fulfilled,其结果才会变成fulfilled,只要有一个处于reject状态,那么这个Promise就是reject状态;

这个方法最常用的地方就是,当我们项目中的某个接口,它的参数取决于其他多个接口的返回,因此这个接口的触发必须需要等所有接口都有返回之后才出发,这样就可以使用Promise.all

Promise.allSetted([p1,p2…,pn])

这个函数的用法和all很接近,也是接收一组Promise作为参数,区别在于,Promise.allSetted的结果是一定的,只要当这组Promise的状态都改变了,不管是fulfilled还是reject,那么这个Promise.allSetted的这个Promise就会变成fulfilled;

Promise.race([p1,p2…,pn])

Prmoise.race()也接收一组Promise作为参数,它代表的是这组Promise有且只要有一个Promise的状态发生变化了,那么Prmoise.race的状态也就跟随其发生变化;

实例方法


promise.then(onFulfilled,onReject)

promise状态改变后的回调,返回新的promise对象

promise.catch(error)

这个catch等同于promise状态为reject时的回调;

promise.then((null,onReject)=>{

// …

})

promise.finally(()=>{})

等同于

promise.then(()=>{

// …

},()=>{

// …

})

这也就导致了,不管promise的状态如何变化,一定会执行函数;

手写Promise

===================================================================

这个环节,主要简单的实现一下Promise,目的是为了更好的了解Promise的细节,实际项目中,我想没有任何一家公司会说,官方的Promise不行,你自己实现一个…这要是存在,太不科学了,还有一种情况,是招聘,但即使是招聘,也很少会遇到说要手写一个Promise,笔试的单位现在都不多了吧,何况还要手写实现一个Promise,太夸张了,面试过程中更多应该是问到原理性质问题。

实现


根据Promises/A+规范,Promise是一个对象或者说是方法,它有一个then方法,有一个catch方法,以及最后一个findlly方法,这里最后一种不弄了,仅以常用的then和catch为例

第一步:定义一个Promise的类,并且它有then方法和catch方法

class Promise {

constructor() {}

then() {}

catch () {}

}

第二步:我们知道Promise有三种状态,分别是:待定(pending),已兑现(fulfilled),已拒绝(rejected),

那么我们自然而言也要定义这三种状态,并且初始状态是pending;

// 状态

const STATUS = {

PENDING: “pending”,

FULFILLED: “fulfilled”,

REJECTED: “reject”

}

// 实现Promise

class Promise {

constructor() {

this.status = STATUS.PENDING;

}

then() {}

catch () {}

}

第三步:根据规范的术语部分,还有一些别的参数,同时Promise接收一个函数作为参数,并且该函数接收两个函数作为参数,这两个函数就是使用Promise时的resolve和reject

// 状态

const STATUS = {

PENDING: “pending”,

FULFILLED: “fulfilled”,

REJECTED: “reject”

}

// 实现Promise

class Promise {

constructor(fn) {

// 状态

this.status = STATUS.PENDING;

// 值

this.value = undefined;

// 原因

this.reason = undefined;

fn(() => {}, () => {})

}

then() {}

catch () {}

}

第四步:这两个参数,第一个可以使当前这个Promise的状态变成fulfilled,第二个可以使当前这个Promise变成reject

// 状态

const STATUS = {

PENDING: “pending”,

FULFILLED: “fulfilled”,

REJECTED: “reject”

}

// 将Promise设置成fulfilled

function fulfilledPromise(promise, value) {

// 这里加一个判断,在规范中说,只有处于pending状态的Promise才可以改变状态

if (promise.status !== STATUS.PENDING) {

return;

}

promise.status = STATUS.FULFILLED;

promise.value = value;

}

// 将Promise设置成reject

function rejectPromise(promise, reason) {

// 同样不能改变

if (promise.status !== STATUS.PENDING) {

return;

}

promise.status = STATUS.REJECTED;

promise.reason = reason;

}

// 实现Promise

class Promise {

constructor(fn) {

// 状态

this.status = STATUS.PENDING;

// 值

this.value = undefined;

// 原因

this.reason = undefined;

fn((value) => {

// 将this传递进去,用于改变当前Promise的状态

// value则是使用promise时,会传递值进去用作后面.then的值,相当于resolve(value)

fulfilledPromise(this, value)

}, (reason) => {

// 和fulfilledPromise一样

rejectPromise(this, reason)

})

}

then() {}

catch () {}

}

在这一步,我们确认了一但稳定了Promise的状态,就不能再改变了;

第五步:实现then方法,then方法在规范上也有定义,then方法有两个参数,一个参数是onFulfilled,它代表状态变成fulfilled时执行,另一个则是onRejected,它代表状态变成reject执行,值的注意的是,这两个方法返回的也是Promise

// 状态

const STATUS = {

PENDING: “pending”,

FULFILLED: “fulfilled”,

REJECTED: “reject”

}

// 将Promise设置成fulfilled

function fulfilledPromise(promise, value) {

// 这里加一个判断,在规范中说,只有处于pending状态的Promise才可以改变状态

if (promise.status !== STATUS.PENDING) {

return;

}

promise.status = STATUS.FULFILLED;

promise.value = value;

// 执行

runCbs(promise.fulfiledCbs, value);

}

// 将Promise设置成reject

function rejectPromise(promise, reason) {

// 同样不能改变

if (promise.status !== STATUS.PENDING) {

return;

}

promise.status = STATUS.REJECTED;

promise.reason = reason;

// 执行

runCbs(promise.rejectedCbs, reason);

}

function isFunction(value) {

return Object.prototype.toString.call(value) === “[object Function]”;

}

function resolvePromise(promise, value) {

}

function rejectPromise(promise, value) {

}

// 执行队列

function runCbs(cbs, value) {

cbs.forEatch(cb => cb(value))

}

// 实现Promise

class Promise {

constructor(fn) {

// 状态

this.status = STATUS.PENDING;

// 值

this.value = undefined;

// 原因

this.reason = undefined;

// then fulfilled的处理队列,因为then可以有很多个,并且按顺序执行

this.fulfiledCbs = [];

// 同样then rejected可能也有很多个…

this.rejectedCbs = [];

fn((value) => {

// 将this传递进去,用于改变当前Promise的状态

// value则是使用promise时,会传递值进去用作后面.then的值,相当于resolve(value)

fulfilledPromise(this, value)

}, (reason) => {

// 和fulfilledPromise一样

rejectPromise(this, reason)

})

}

// 两个参数,一个参数是onFulfilled,一个参数是onRejected

then(onFulfilled, onRejected) {

const promiseCurrent = this;

const promiseReturn = new Promise(() => {});

// then的执行取决于当前Promise的状态

// 状态等于fulfilled

if (promiseCurrent.status === STATUS.FULFILLED) {

// 判断是onFulfilled是否是函数

if (!isFunction(onFulfilled)) {

// 将老的直接返回出去

return promiseCurrent;

}

// 使用setTimeout模拟异步

setTimeout(() => {

try {

// 值传进去,并且实现reslove(value)

const value = onFulfilled(promiseCurrent.value);

resolvePromise(promiseReturn, value)

} catch (error) {

rejectPromise(promiseReturn, error);

}

}, 0)

}

// 状态等于reject

if (promiseCurrent.status === STATUS.REJECTED) {

// 判断是onRejected是否是函数

if (!isFunction(onRejected)) {

// 将老的直接返回出去

return promiseCurrent;

}

// 使用setTimeout模拟异步

setTimeout(() => {

try {

// 值传进去,并且实现reslove(value)

const reason = onRejected(promiseCurrent.value);

resolvePromise(promiseReturn, reason)

} catch (error) {

rejectPromise(promiseReturn, error);

}

}, 0)

}

// 状态等于pending

if (promiseCurrent.status === STATUS.PENDING) {

// 如果状态处于pending状态,那么代表第一个Primise还处于处理中的状态,必须要等到状态稳定

// 将成功的存入队列,同时要模拟异步

promiseCurrent.fulfiledCbs.push(setTimeout(() => {

try {

// 值传进去,并且实现reslove(value)

const value = onFulfilled(promiseCurrent.value);

resolvePromise(promiseReturn, value)

} catch (error) {

rejectPromise(promiseReturn, error);

}

}), 0)

// reject也要存,同时要模拟异步

promiseCurrent.rejectedCbs.push(setTimeout(() => {

try {

// 值传进去,并且实现reslove(value)

const reason = onRejected(promiseCurrent.value);

resolvePromise(promiseReturn, reason)

} catch (error) {

rejectPromise(promiseReturn, error);

}

}), 0)

}

return promiseReturn

}

catch () {}

}

第六步:其实到这里,then方法已经差不多了,唯一差的就还是resolvePromise这两个,这两个的作用是对Promise进行解析,这个解析的过程就相对比较复杂了;另外,这一部分解析其实在规范里面都有写流程,代码就是根据这个流程实现的;

// 状态

const STATUS = {

PENDING: “pending”,

FULFILLED: “fulfilled”,
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数前端工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

《JavaScript》异步编程(三)深入浅出Promise(3)

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!

《JavaScript》异步编程(三)深入浅出Promise(3)

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:前端)

《JavaScript》异步编程(三)深入浅出Promise(3)

结尾

学习html5、css、javascript这些基础知识,学习的渠道很多,就不多说了,例如,一些其他的优秀博客。但是本人觉得看书也很必要,可以节省很多时间,常见的javascript的书,例如:javascript的高级程序设计,是每位前端工程师必不可少的一本书,边看边用,了解js的一些基本知识,基本上很全面了,如果有时间可以读一些,js性能相关的书籍,以及设计者模式,在实践中都会用的到。

资料领取方式:戳这里免费获取

resolvePromise(promiseReturn, reason)

} catch (error) {

rejectPromise(promiseReturn, error);

}

}), 0)

}

return promiseReturn

}

catch () {}

}

第六步:其实到这里,then方法已经差不多了,唯一差的就还是resolvePromise这两个,这两个的作用是对Promise进行解析,这个解析的过程就相对比较复杂了;另外,这一部分解析其实在规范里面都有写流程,代码就是根据这个流程实现的;

// 状态

const STATUS = {

PENDING: “pending”,

FULFILLED: “fulfilled”,
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数前端工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

[外链图片转存中…(img-6FNXIB36-63)]

[外链图片转存中…(img-XCtQpukM-64)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!

[外链图片转存中…(img-UUufyuPk-65)]

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:前端)

[外链图片转存中…(img-Lcdy2Ilj-65)]

结尾

学习html5、css、javascript这些基础知识,学习的渠道很多,就不多说了,例如,一些其他的优秀博客。但是本人觉得看书也很必要,可以节省很多时间,常见的javascript的书,例如:javascript的高级程序设计,是每位前端工程师必不可少的一本书,边看边用,了解js的一些基本知识,基本上很全面了,如果有时间可以读一些,js性能相关的书籍,以及设计者模式,在实践中都会用的到。

资料领取方式:戳这里免费获取

html5

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/148900.html

(0)
上一篇 2025-03-27 18:15
下一篇 2025-03-27 18:20

相关推荐

发表回复

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

关注微信