你可能知道,Javascript语言的执行环境是"单线程"(single thread)。javascript
所谓"单线程",就是指一次只能完成一件任务。若是有多个任务,就必须排队,前面一个任务完成,再执行后面一个任务,以此类推。java
这种模式的好处是实现起来比较简单,执行环境相对单纯;坏处是只要有一个任务耗时很长,后面的任务都必须排队等着,会拖延整个程序的执行。常见的浏览器无响应(假死),每每就是由于某一段Javascript代码长时间运行(好比死循环),致使整个页面卡在这个地方,其余任务没法执行。es6
为了解决这个问题,Javascript语言将任务的执行模式分红两种:同步(Synchronous)和异步(Asynchronous)。编程
回调是异步编程最基本的方法。api
假定有两个函数f1和f2,后者等待前者的执行结果。promise
f1();
f2();
复制代码
若是f1是一个很耗时的任务,能够考虑改写f1,把f2写成f1的回调函数。浏览器
function f1(callback){ setTimeout(function () { // f1的任务代码 callback(); }, 1000); } 复制代码
执行代码就变成下面这样bash
f1(f2);
复制代码
采用这种方式,咱们把同步操做变成了异步操做,f1不会堵塞程序运行,至关于先执行程序的主要逻辑,将耗时的操做推迟执行。 回调函数的优势是简单、容易理解和部署,缺点是不利于代码的阅读和维护,各个部分之间高度耦合,流程会很混乱,并且每一个任务只能指定一个回调函数。markdown
Promises
对象是CommonJS
工做组提出的一种规范,目的是为异步编程提供统一接口。并发
简单说,它的思想是, 每个异步任务返回一个Promise对象,该对象有一个then方法,容许指定回调函数。 Promises的出现大大改善了异步变成的困境,避免出现回调地狱,嵌套层级获得改善。
具体api的介绍请看 阮一峰 大神的 ECMAScript 6 入门 在这我举几个简单的场景的实现
为了使代码简介,promise的rejected
状态的相关reject()和catch()方法省略
// 1请求 function getData1 () { return new Promise(function (resolve, reject) { setTimeout(() => { console.log('1执行了') resolve('请求到模拟数据1111拉') }, 2000) }) } // 2请求 function getData2 (params) { return new Promise(function (resolve, reject) { setTimeout(() => { console.log('2执行了') resolve('请求到模拟数据22222拉!params:' + params) }, 1500) }) } 复制代码
1请求完成后,把1的响应参数传入2,在发2请求
function promiseDemo () { getData1() .then(res => { return getData2(res) }) .then(res => { console.log(res) }) } promiseDemo() // 1执行了 // 2执行了 // 请求到模拟数据22222拉!params:请求到模拟数据1111拉 用时 3500 ms 复制代码
1请求、2请求同时发,两条响应都收到后在执行
function promiseDemo () { Promise.all([getData1(), getData2()]).then(function (res) { console.log(res) }) } // 2执行了 // 1执行了 // ["请求到模拟数据1111拉", "请求到模拟数据22222拉!params:undefined"] 用时 2000 ms 复制代码
1请求、2请求同时发,其中一条收到请求就执行
function promiseDemo () { Promise.race([getData1(), getData2()]).then(function (res) { console.log(res) }) } // 2执行了 // 请求到模拟数据22222拉!params:undefined 用时 1500 ms // 1执行了 复制代码
由此Promise对象仍是很好用的,对于异步的流程的控制获得了大大改善,经过
.then()
的方法可进行链式调用。 但是.then() .catch()
的使用也致使代码很是难看,嵌套也很深,因此async/await
就出来了
Async/await 是Javascript编写异步程序的新方法。以往的异步方法无外乎回调函数和Promise。可是Async/await创建于Promise之上。
咱们仍是来看一 看阮一峰 大神的 ECMAScript 6 入门 的例子
async function timeout(ms) { await new Promise((resolve) => { setTimeout(resolve, ms); }); } async function asyncPrint(value, ms) { await timeout(ms); console.log(value); } asyncPrint('hello world', 50); 复制代码
上面代码指定50毫秒之后,输出hello world。 进一步说,async函数彻底能够看做多个异步操做,包装成的一个 Promise 对象,而await命令就是内部then命令的语法糖
咱们看具体的示例
1请求完成后,把1的响应参数传入2,在发2请求
上文中的promise 实现方法是经过then的链式调用,可是采用async会更加简洁明了
async function asyncDemo () { const r1 = await getData1() const r2 = await getData2(r1) console.log(r2) } // 1执行了 // 2执行了 // 请求到模拟数据22222拉!params:请求到模拟数据1111拉 用时 3500 ms 复制代码
用同步的书写方式实现了异步的代码。等待getData1的异步函数执行完了后发返回值赋值给r1,传入r2,在执行r2
1请求、2请求同时发,规定请求到达的顺序
假如咱们有一种这样的业务需求,并发两个请求,可是要规定收到请求的顺序应该怎么作的?这里仍是借鉴阮一峰大神的代码
async function asyncDemo2 () { const arr = [getData1, getData2] const textPromises = arr.map(async function (doc) { const response = await doc() return response }) // 按次序输出 for (const textPromise of textPromises) { console.log(await textPromise); } } // 2执行了 (由于2是 1500ms后执行) 因此2先执行 // 1执行了 // 请求到模拟数据1拉 (for .. of )规定了输出的顺序 // 请求到模拟数据22222拉!params:undefined 复制代码
面代码中,虽然map方法的参数是async函数,但它是并发执行的,由于只有async函数内部是继发执行,外部不受影响。后面的for..of循环内部使用了await,所以实现了按顺序输出
怎么样这么BT的需求都能实现把。
他使得异步代码变的再也不明显也是一点弊端咯,不过根据实际状况选择最合适的异步编程才是最好的选择。async 是 Generator 函数的语法糖。因此想更深刻的理解其中内部原理的赶忙去看看 Generator
函数把