在 《Puppteer 实现 web 自动测试》 一文里,可以看出来如果 Puppteer 脚本用 async/await 和 promise的一些写法,会让脚本非常易懂,而且符合我们人工操作浏览器的习惯。所以为了写好 Puppteer 脚本,需要先对 async/await 的概念有所了解。 一个比方 你和老王打赌:明天是否下雨。如果你赢了,老王给你10个亿;如果你输了,你给老王10块钱。 如果用一块展板描述这个赌局,可以是这样: 【明日下雨】 老王赌局 —————————— 赢: +10亿 输: –10元 转眼跟小王打赌: 【明日下雨】 小王赌局 —————————— 赢: 被请KFC 输: 请KFC 由上可见, 赌局 和 赌注 是无关的。比如,同样是明天下雨,另一场赌局可能如下: 由此得到【规则1】 规则1:输赢规则和赌注无关,可以分离 当赌局完成后,只要出现了一种情况,那么另一种情况必然不会同时发生,比如不可能既下雨又不下雨。由此得到【规则2】 规则2:只能有一种结果出现。 如果你和老王商量好了:如果明天下雨,老王付你10块钱。到了第二天果然下雨,老王反悔了,想修改输赢规则,你一定是不同意的,这就叫买定离手。由此得到【规则3】 规则3:输赢结果一旦出现,则不能更改。 如何描述 用一段伪代码描述上面的下雨赌局,可以这么写 赌局(赢,输){ 到明天() 如果下雨{ 赢() }如果没下{ 输() } } 到明天 函数可以替换成 抛硬币(), 或者是 参加考试(),这种负责产生结果的函数,可以称之为"生产者"。 赢 函数 可以是 {得5元} ,或者是 {被请KFC},这种针对结果的处理方案,可以称之为"消费者"。 这种"生产者"和"消费者"分离的状态,对应了上面的 规则 1 而赌局本身,既不产生结果,也不消费结果,只是将无关的二者结合在一起,并进行一次。将赌局抽象出来,就是 Promise 。 生产者(抛硬币)→ Promise → 消费者(赌注) 所以 Promise 可以理解为一个中介(Proxy),一个 Promise 对象用下面代码生成: let promise = new Promise(function(resolve, reject) { // 生产者代码 }); 当 promise 对象生成时,将执行 生产者代码,并生产出结果。Promise 接收一个函数作为参数,这个函数的两个参数分别对应成功和失败的消费者函数,消费前面生产出的结果。 生产者代码在得出结果后,应该根据结果的不同,调用对应的消费者函数(成功则调用 resolve,失败则调用 reject),同时将结果作为消费者函数的参数,从 promise 对象中传递出去。 举个例子,最简单的生产者代码,恐怕就是"什么也不做"了,直接判定为成功,只有resolve一种结果。 let promise = new Promise(function(resolve, reject) { resolve("done") }); 这段代码执行后,对象 promise 的状态就固定为成功(fulfilled),而且不能改变。当然也可以直接判定失败: let promise = new Promise(function(resolve, reject) { reject("lose") }); 这段代码执行后,对象 promise的状态就固定为失败(rejected) Await 生产者代码除了生产成功或者失败的结果,往往还有其他类型的结果输出,比如文件内容、或者新的对象实例,等等。所以需要通过 resolve 函数将输出的结果传递出来。 await 可以在外面接纳这个结果。 Promise → resolve → await 比如,可以这么使用 await : let promise = new Promise(function(resolve, reject) { resolve("done") }); let result = await promise; console.log(result); // "done" 这个例子里,我们可以从控制台里看到 “done” 的输出, await 一直等待 promise 的成功结果,一旦等到,就将 resolve 的参数返回给 result 变量 Async 要想使用 await 关键字,还必须声明 await 所在的函数为 async,用法很简单,只要在 function 前面加上 async 就行。 async function(){ await … } async function(){ let promise = new Promise(function(resolve, reject) { resolve("done") }); let result = await promise; console.log(result); // "done" } 函数声明之前加上 async 后, 即使不用明确的声明 return,函数总会隐含的返回一个 Promise。如果明确的写明 return 一个值,函数也仍然返回一个 Promise,然后使这个 Promise 成功 resolve 那个值。比如下面的例子: async function(){ //其他代码 return 1 } 等效于 async function(){ //其他代码 return Promise.resolve(1) } 这个返回值是可以被 await 等待得到的。 理解了上面的内容,我们就可以继续用 puppeteer 编写自动脚本了