链接:https://juejin.im/post/5c30375851882525ec200027
1.JS 异步编程进化史:callback -> promise -> generator -> async + await
2.async/await 函数的实现,就是将 Generator 函数和自动执行器,包装在一个函数里。
3.async/await可以说是异步终极解决方案了。
(1) async/await函数相对于Promise,优势体现在:
处理 then 的调用链,能够更清晰准确的写出代码并且也能优雅地解决回调地狱问题。当然async/await函数也存在一些缺点,因为 await 将异步代码改造成了同步代码,如果多个异步代码没有依赖性却使用了 await 会导致性能上的降低,代码没有依赖性的话,完全可以使用 Promise.all 的方式。
(2) async/await函数对 Generator 函数的改进,体现在以下三点:
内置执行器。 Generator 函数的执行必须靠执行器,所以才有了 co 函数库,而 async 函数自带执行器。也就是说,async 函数的执行,与普通函数一模一样,只要一行。
更广的适用性。 co 函数库约定,yield 命令后面只能是 Thunk 函数或 Promise 对象,而 async 函数的 await 命令后面,可以跟 Promise 对象和原始类型的值(数值、字符串和布尔值,但这时等同于同步操作)。
更好的语义。 async 和 await,比起星号和 yield,语义更清楚了。async 表示函数里有异步操作,await 表示紧跟在后面的表达式需要等待结果。
一、回调(实现简单、易于理解。但不利于后面的维护等,不可用 try-catch 去捕获错误,也不可以直接 return 出来!!!) 1、
ajax(url, () => { // 处理逻辑 ajax(url1, () => { // 处理逻辑 ajax(url2, () => { // 处理逻辑 }) }) })二、监听(异步任务的执行不取决于代码的顺序,而取决于某个事件是否发生。也易于理解,可同时指定多个回调函数,利于模块化。但是 就变成了事件驱动(‘一件事的完成出发了其他事件的执行’),很难看出 主流程是啥!) 1、
//Jq实现的(伪代码),直接不可运行 f1.on('done', f2); function f1() { setTimeout(function () { // ... f1.trigger('done'); }, 1000); }三、发布订阅(与事件监听类似,但是相对好些。因为可以通过查看“消息(监听)中心”,了解存在多少信号、每个信号有多少订阅者,从而监控程序的运行。) 1、
//jQuery.publish('done')的意思是,f1执行完成后,向信号中心jQuery发布done信号,从而引发f2的 //执行。 f2完成执行后,可以取消订阅(unsubscribe) jQuery.subscribe('done', f2); function f1() { setTimeout(function () { // ... jQuery.publish('done'); }, 1000); } jQuery.unsubscribe('done', f2);
四、Promise(一般用于 网络请求、读取本地文件等较长时间的操作,可链式调用,解决地狱回调问题。但是状态一旦改变就不能再变了,不可取消promise、错误需要通过回调函数(catch???)捕获。
1、
let p = new Promise((resolve, reject) => { reject('reject') resolve('success')//无效代码不会执行 }) p.then( value => { console.log(value) }, reason => { console.log(reason)//reject } ) //链式调用 // 例1 Promise.resolve(1) .then(res => { console.log(res) return 2 //包装成 Promise.resolve(2) }) .catch(err => 3) .then(res => console.log(res))
五、生成器Generators/ yield(语法行为与传统函数完全不同,强在可以控制函数的执行。比较抽象难懂)
1、简介
语法上,首先可以把它理解成,Generator 函数是一个状态机,封装了多个内部状态。Generator 函数除了状态机,还是一个遍历器对象生成函数。可暂停函数, yield可暂停,next方法可启动,每次返回的是yield后的表达式结果。yield表达式本身没有返回值,或者说总是返回undefined。next方法可以带一个参数,该参数就会被当作上一个yield表达式的返回值。2、
1、function *foo(x) { let y = 2 * (yield (x + 1)) let z = yield (y / 3) return (x + y + z) } let it = foo(5) console.log(it.next()) // => {value: 6, done: false} console.log(it.next(12)) // => {value: 8, done: false} console.log(it.next(13)) // => {value: 42, done: true} 2、function* r() { let r1 = yield read('./1.txt') let r2 = yield read(r1) let r3 = yield read(r2) console.log(r1) console.log(r2) console.log(r3) } let co = require('co') co(r()).then(function(data) { console.log(data) }) // 2.txt=>3.txt=>结束=>undefined 3、function *fetch() { yield ajax(url, () => {}) yield ajax(url1, () => {}) yield ajax(url2, () => {}) } let it = fetch() let result1 = it.next() let result2 = it.next() let result3 = it.next()
六、async/await(await后面跟的、async返回的一般是 promise实例对象)
1、简介
async/await是基于Promise实现的,它不能用于普通的回调函数。async/await与Promise一样,是非阻塞的。async/await使得异步代码看起来像同步代码,这正是它的魔力所在。一个函数如果加上 async ,那么该函数就会返回一个 Promise
2、
1、async function async1() { return "1" } console.log(async1()) // -> Promise {<resolved>: "1"} 2、let fs = require('fs') function read(file) { return new Promise(function(resolve, reject) { fs.readFile(file, 'utf8', function(err, data) { if (err) reject(err) resolve(data) }) }) } async function readResult(params) { try { let p1 = await read(params, 'utf8')//await后面跟的是一个Promise实例 let p2 = await read(p1, 'utf8') let p3 = await read(p2, 'utf8') console.log('p1', p1) console.log('p2', p2) console.log('p3', p3) return p3 } catch (error) { console.log(error) } } readResult('1.txt').then( // async函数返回的也是个promise data => { console.log(data) }, err => console.log(err) ) // p1 2.txt // p2 3.txt // p3 结束 // 结束 3、并发(合并毫无关系的文件)请求 let fs = require('fs') function read(file) { return new Promise(function(resolve, reject) { fs.readFile(file, 'utf8', function(err, data) { if (err) reject(err) resolve(data) }) }) } function readAll() { read1() read2()//这个函数同步执行 } async function read1() { let r = await read('1.txt','utf8') console.log(r) } async function read2() { let r = await read('2.txt','utf8') console.log(r) } readAll() // 2.txt 3.txt