写在后面
javascript 语言的执行环境是 ” 单线程 ”(single thread),就是指一次只能实现一件工作。如果有多个工作,就必须排队,等后面一个工作实现,再执行前面一个工作,以此类推。
这种模式的益处是实现起来比较简单,执行环境绝对单纯;害处是只有有一个工作耗时很长,前面的工作都必须排队等着,会迁延整个程序的执行。
单线程
function f1() {console.log('1')
}
function f2() {console.log('2')
}
f1()
f2()
很容易能够看出,上述代码会顺次输入 1,2。因为代码是从上到下,顺次执行,执行完 f1(),才会执行 f2()。然而如果 f1()中的代码执行的是读取文件或者 ajax 操作呢,文件的读取都须要肯定工夫,难道咱们须要齐全等到文件齐全读完再进行写操作么?为了解决这个问题,接下来咱们来探索一下 js 中 同步和异步 的概念。
同步和异步
同步
- 指在 主线程 上排队执行的工作,只有前一个工作执行结束,能力继续执行下一个工作。
- 也就是调用一旦开始,必须这个调用 返回后果(划重点——)能力持续往后执行。程序的执行程序和工作排列程序是统一的。
异步
- 异步工作是指不进入主线程,而进入 工作队列 的工作,只有工作队列告诉主线程,某个异步工作能够执行了,该工作才会进入主线程。
- 每一个工作有一个或多个 回调函数。前一个工作完结后,不是执行后一个工作, 而是执行回调函数,后一个工作则是不等前一个工作完结就执行。
- 程序的执行程序和工作的排列程序是 不统一 的,异步的。
- 咱们罕用的 setTimeout 和 setInterval 函数,Ajax 都是异步操作。
那么如何实现异步编程呢,笔者介绍几种办法
Web 前端视频解说:进入学习
回调函数(Callback)
回调函数,这是异步编程最根本的办法。
const fs = require('fs')
fs.readFile('./pakage.json',(err,info) => {fs.writeFile('./p.json',info,(err) => {if(!err) {setTimeout(() => {console.log('ok')
},2000)
}
})
})
上述代码通过回调函数的嵌套,从文件系统中读取一个./pakage.json 文件并写入./p.json, 读取胜利两秒后输入 ’ok’。用回调来实现异步,没有什么问题。
然而试想,如果再多几个异步函数,代码整体的维护性,可读性都变的极差,如果出了 bug,修复过程也变的极为艰难,这个便是所谓的 回调函数天堂。
Promise 对象
Promise 对象用于示意一个异步操作的最终状态(实现或失败),以及其返回的值。
MDN 对 Promise 定义如上,Promise 本意为 承诺,咱们能够了解为程序承诺过一段时间后会给你一个后果。
Promise 是一个对象,能够保留三个状态 每一时刻必须有一个状态。
- 胜利 Fulfilled
- 失败 Rejected
- 解决中 Pending
- 默认 pending 如果调用 resolve fulfilled
-
默认 pending 如果调用 reject rejeced
const fs = require('fs')
const promise1 = new Promise((resolve,reject) => {fs.readFile('./package.json',(err,info) => {resolve(info)
})
})
const promise2 = (info) => {new Promise((resolve,reject) => {fs.writeFile('./p.json', info,(err) => {if(!err) {resolve();
}else{reject();
}
})
})
}
const promise3 = (time) => {return new Promise((resolve,reject) => {setTimeout(() => {resolve()
},time)
})
}
//then 链式调用
// 读文件胜利 将后果作为参数传入 promise2
promise1.then((info) => {return promise2(info)
})
.then(() => {
// 等着后面的 promise
console.log('读写实现')
return promise3(2000)
})
.then(()=> {console.log('ok')
})
这么一看,并没有什么区别,还比下面的异步回调简单,得先新建 Promise 再定义其回调。但其实,Promise 的真正弱小之处在于它的多重链式调用,能够防止层层嵌套回调。
咱们先应用 new
来构建一个promise
。Promise 承受一个函数作为参数,该函数的两个参数别离是 resolve 和 reject。resolve
:胜利时调用,并将后果,作为参数传递进来;reject
:失败时调用,并将谬误,作为参数抛出。
- then 办法接管两个函数作为参数,第一个参数是 Promise 执行胜利时的回调,第二个 参数是 Promise 执行失败时的回调。
- Promise 对象的 then 办法返回一个新的 Promise 对象,因而所以能够通过 链式调用then 办法。
咱们还能够持续优化一丢丢。
async+await 语法糖
间接上代码
async function run() {
let info = await promise1;
await promise2(info);
await promise3(2000);
console.log('ok');
}
async 函数是在 ES2017 规范中引入的,使咱们异步的代码更加优雅了。这里应用 async+await 代替了.then() 办法。
- async 必须在函数申明前
- await 接一个 promise,那么前面的代码就会期待,等 promise resolve 了才会执行。