写在后面

 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
  1. 默认 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了才会执行。