本篇采用倒叙法,将废话放在片尾
原因:JavaScript在浏览器中顺序而异步地执行,导致某些需要有前后顺序要求的操作失效。比如先要通过XHR获得token,再用token访问新网页,异步执行会导致访问新网页的时候用的token可能还是undefined
,即此时XHR请求还没完成。
解决方案:Promise
对象
参考了廖雪峰老师的博客:https://www.liaoxuefeng.com/wiki/1022910821149312/1023024413276544
Promise
的使用,在下将其归类为两种操作,创建和运行。一种创建Promise
对象,另一种对Promise
对象进行操作:进入下一状态或者得出结果。
Promise对象的创建:两种方法,一个是直接new
,另一个利用Promise.resolve()
或者Promise.reject()
是创建有状态的Promise。后者两者分别对应一个Promise
对象在执行成功和出错时的返回结果。从语法上允许直接向上述两个Promise
方法传递一个Object,作为一个新Promise
的返回结果,但是从使用上一般都是作为管道,即将结果从一个Promise
传递到另一个Promise
,不会用于一个全新Promise
的生成。
将操作写入到Promise
对象的函数中,然后就可以“运行”该Promise
对象。Promise
对象有一个then()
方法,该方法接受一个函数作为参数,执行的工作是将传入Promise
的函数体执行后,将(如果存在的)resolve
的参数值作为then()
方法内传入的函数的第一个参数,然后执行。结合代码解释如下:
function addBy1(a) {
return new Promise((resolv, reject) => {
resolv(a + 1);
})
}
var p = addBy1(0);
首先在此创建了一个函数,命名为addBy1
,接受一个参数,结合函数体可以大致猜出,该函数的功能是将传入a
的值加1。
Promise
对象有两种自用的返回值的方法:resolve()
和reject()
,可以分别理解为带成功和失败状态的返回结果。在直接使用new
关键字生成一个新的Promise
对象时,这两个函数以参数的形式传入函数体,供不同条件下的返回使用。
实际上,可以直接将addBy1
的return语句中的部分用于变量p
的赋值。但是为何在这里分开呢?下面自有用处。(突然跑题)
显然,若是将addBy1
函数就视作一个将传入数字加1的功能的话,应该得到相应的结果。如何得到呢?答案是使用Promise
对象的运行功能。(对=>
运算符略作解释:该符号用于简化匿名函数的表达,(参数列表)=>{函数体}
)
p.then(
(a) => {
console.log(a);
}
);
然后就可以获得梦寐以求的1了。但是,显然功能不会就这么简单。Promise
的妙处就在于这个then()
,简要来说,它提供了在异步执行的JavaScript中执行一系列的同步操作的能力。正是有了这个功能,使得需要依赖前一个操作完成后再执行下一个操作的逻辑流程能够按照正确的时序图执行。直观地感受一下:
var p = addBy1(0);
p.then(addBy1).then(addBy1).then(addBy1).then(addBy1).then(
(a) => {
console.log(a);
}
);
在执行了这一段代码后,会发现输出的结果是5:从创建变量开始,一共执行了5次addBy1
函数,而初始值为0。
但是若是再使用上面的一段代码输出p中的返回值,会发现其仍然是1。理解为then()
方法不会改变原始Promise
对象的状态吧。
then()
函数接收的参数若是返回Promise
对象,就可以在后面继续接.then()
,从而可以定义无限长的.then()
序列。而这些then()
方法又必须串行执行。这就达到了一开始的目的:串行地执行JavaScript代码。
这么一说,操作都是n+1,怎么看得出来到底是不是1+1,2+1一直到4+1呢?虽然从逻辑上似乎也找不出其他的可行执行方法,但是可以通过在第一段代码的resolv()
函数之前加一句console.log(a);
来验证,显然输出结果会是01234。因而拓展一下,将简单的1+1操作换为需要较长时间的,比如XHR请求一类的操作的话,就可以方便地实现时序的可控化了。
最后,上文不是有提到为什么要将addBy1
操作单独提出作为一个函数吗。原因有二,其一就是上文已经用到了的,为了重复进行Promise
的操作,其二就是,这是一个极好的函数式编程的入门范例。函数式编程的思想之一就是使用尽可能少的参数。比如传统的1+1会表示为var result = plus(1, 1)
,而函数式编程会这么想:
class Number {
constructor(x) {this.number = x;}
addBy1() {
return this.number + 1;
}
}
即在正常使用时,会这么用:
var a = new Number(1);
var result = a.addBy1();
从而得到2。在使用过程中,除开对象的声明之外,没有用到一个有参数的方法。
最近处于各种原因,开始了前端编程。。然后上手就是微信(诶,好像不对)丁丁(也不对)钉钉(啊对,就是这个了)小程序。不出所料,工作就是生物代码粘合器(2077警告)。这里即兴赋歌一段:
我有个appId
我有个appSecret
(双手做插合状,并摆出夸张的面部表情)
access_token
!
我有个access_token
我有个authCode
(双手做插合状,并摆出夸张的面部表情)
userId
!
(大雾)
虽然对于很多干这行的人来说,可能认为在下这是对工作本身的不尊重,但是这确实反映了在下在看文档的时候,看到为了获取一个userId
而不得不从appId
和appSecret
开始一路以token换token最终拿到所需要的数据的过程中,哭笑不得的感觉。我当时不选择做前端,大概就有一部分原因在这。虽然知道出于安全角度考虑,这是较为稳妥的一种方式,但是如果深陷其中,并以其为日常的工作,显然就太有些小看程序员的能力了。(咳咳,但是砖总得要有人去搬啊)