写在后面
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都是异步操作。
那么如何实现异步编程呢,笔者介绍几种办法
回调函数(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链式调用//读文件胜利 将后果作为参数传入promise2promise1.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了才会执行。