我正在参与掘金创作者训练营第5期,点击了解活动详情
异步
- 一个任务分为两段来执行
- 比如读取一个文件 读取文件是需要时间的 那么就可以在读取文件的时候进行其他任务
- 等到读取完文件 在接着执行刚才还没有执行完的操作
- 这种不连续的执行 就叫异步。相反 连续的 就叫同步
回调函数
-
回调函数,就是把任务的第二段单独写在一个函数里,等到重新执行这个任务的时候,就直接调用这个函数。callback => 重新调用
-
比如读取文件 可以这样写
-
fs.readFile('/text', function (err, data) { if (err) throw err console.log(data) })
-
Promise
-
回调函数本身没有问题,问题是回到函数的嵌套,也就是经常提到的回调地狱
-
如果依次读取多个文件,就会出现多重嵌套。代码不是纵向发展,而是横向发展。
-
所以 promise 的出现就是为了解决上述问题。但是这并不是一种新的语法功能,而是一种新的写法。
-
readFile(fileA) .then(res => { console.log(res) }) .then(res => { })
-
-
所以 promise 的写法只是回调函数的改进,使用 .then() 方法以后,异步任务的两段可以看的更加清楚
- 就是第一段执行的返回结果得到后传给 then 然后 then 方法中执行的是第二段
-
存在的问题是 代码冗余了 会有很多 .then 并且每个函数都要使用 promise 包裹一下。但是封装一些 axios 请求的时候会非常方便
generator 函数
-
Generator 函数是协程在 ES6 的实现,最大特点就是可以交出函数的执行权(即暂停执行)
-
function* gen(x){ var y = yield x + 2; return y; } - 使用 * 来区别于 普通函数 * 和空格 位置 不做要求
-
-
整个 Generator 函数就是一个封装的异步任务,或者说是异步任务的容器。异步操作需要暂停的地方,都用 yield 语句注明
- 其实也就是一个生成器 (可以去看看 js 红宝书 生成器和迭代器这一块)
- Generator 函数可以暂停执行和恢复执行,这是它能封装异步任务的根本原因。
async 函数是什么
- async 函数就是 Generator 函数的语法糖
-
var fs = require('fs'); var readFile = function (fileName){ return new Promise(function (resolve, reject){ fs.readFile(fileName, function(error, data){ if (error) reject(error); resolve(data); }); }); }; var gen = function* (){ var f1 = yield readFile('/etc/fstab'); var f2 = yield readFile('/etc/shells'); console.log(f1.toString()); console.log(f2.toString()); }; var asyncReadFile = async function (){ var f1 = await readFile('/etc/fstab'); var f2 = await readFile('/etc/shells'); console.log(f1.toString()); console.log(f2.toString()); }; - 比较就会发现 Generator 函数就是将 * 替换成 async,yield 替换成 await
async 函数的实现
-
async 函数的实现,就是将 Generator 函数和自动执行器,包装在一个函数里
-
async function fn(args){ // ... } // 等同于 function fn(args){ return spawn(function*() { // ... }); } -
所有的 async 函数都可以写成上面的第二种形式,其中的 spawn 函数就是自动执行器
-
而 spawn 函数是基于 promise 函数实现的
-
unction spawn(genF) { return new Promise(function(resolve, reject) { var gen = genF(); function step(nextF) { try { var next = nextF(); } catch(e) { return reject(e); } if(next.done) { return resolve(next.value); } Promise.resolve(next.value).then(function(v) { step(function() { return gen.next(v); }); }, function(e) { step(function() { return gen.throw(e); }); }); } step(function() { return gen.next(undefined); }); }); }
-
-
基本使用
-
async + 函数名 是用来定义一个异步函数的 得到一个 promise 对象
- 函数执行的时候,一旦遇到 await 就会先返回,等到触发的异步操作完成,再接着执行函数体内后面的语句。
-
await + 任意函数表达式 一般使用 promise 表达式
-
它的返回值是 promise.resolve() 或者 promise.reject()
-
因为会返回错误信息 promise.reject() 所以最好用 try catch 捕获一下
- 就是 await 使用 try catch 捕获一下
- 作用是 不会导致程序中断 即使出错了也会继续执行下去
-
-
例子
-
async function getStockPriceByName(name) { var symbol = await getStockSymbol(name); // 两个异步函数 返回的是 promise 对象 通过 await 获取结果 var stockPrice = await getStockPrice(symbol); return stockPrice; // 返回的是一个 Promise 对象 } getStockPriceByName('goog').then(function (result){ console.log(result); }); - 上面代码是一个获取股票报价的函数,函数前面的async关键字,表明该函数内部有异步操作。调用该函数时,会立即返回一个Promise对象。
-
function timeout(ms) { return new Promise((resolve) => { setTimeout(resolve, ms); }); } async function asyncPrint(value, ms) { await timeout(ms); console.log(value) } asyncPrint('hello world', 50); - 上面代码指定50毫秒以后,输出"hello world"
-
-
总结一下写法
-
function timeout(ms) { return new Promise((resolve) => { setTimeout(resolve, ms); }); } let promiseRes = async foo() { let res = await timeout(50) // 返回一个 promise 的异步函数 return res } promiseRes.then(res => {}) // 如果外层还有一个 async 函数 let asyncRes = await foo()
-
优点
- 属于 es7 的语法 编写方便 提高效率
- 以后肯定是使用异步方法的多 而使用 async await 可以让异步函数看上去像同步函数
- 更好的语义。 async 和 await,比起星号和 yield,语义更清楚了。async 表示函数里有异步操作,await 表示紧跟在后面的表达式需要等待结果。
补充
- promise 和 async 的区别
- 他们做的事情是相同的 paomise 自己的方法会多一些 all race 等
- async 需要自己定义 catch