标签:
我了解到的node异步编程可分成:
1.回调函数
2.pub/sub模式(发布/订阅模式)
3.promise
4.generator
5.async await
该方法是最直接常用的异步操作方式,比如在setInterval 和 ajax等会使用到, 存在缺点有:
1.代码不易阅读并且容易出现金字塔嵌套问题;
2.一般只能对应一个回调函数(一对一),使用上有一定的限制;
fs.readFile(‘/etc/passwd‘, function (err, data) { if (err) throw err; console.log(data); });
函数先读取/etc/passwd,再执行回调函数,在这两者之间抛出的错误会以参数形式传入回调函数中。
该方法不再局限于一对一的形式,以多对多的形式监听事件,可以很方便的订阅和取消订阅存在缺点有:需要借助类库(jQuery),事件与回调函数的顺序很重要。
回调函数的缺点之一是容易出现函数多层嵌套,难以维护的场面.而es6语法中的promise正好解决这类问题. Promise包含三种状态:pending、fulfilled、rejected,三种状态只能发生两种转换(从pending—>fulfilled、pending—>rejected),并且状态的转换仅能发生一次。
Promise实例生成以后,可以用then方法分别指定Resolved状态和Reject状态的回调函数:
then方法可以接受两个回调函数作为参数。第一个回调函数是Promise对象的状态变为Resolved时调用,第二个回调函数是Promise对象的状态变为Reject时调用。
a) then方法返回Promise。这样就实现了多个异步操作的串行操作。
b)实现了多个不同异步库之间的转换。
var p1 = new Promise(function (resolve, reject) { setTimeout(() => reject(new Error(‘fail‘)), 3000) }) var p2 = new Promise(function (resolve, reject) { setTimeout(() => resolve(p1), 1000) }) p2.then(result => console.log(result)) p2.catch(error => console.log(error)) // Error: fail
Promise.all方法用于将多个Promise实例,包装成一个新的Promise实例。是我们项目中正在使用的方式。如var p = Promise.all([p1,p2,p3]);
p的状态由p1、p2、p3决定,分成两种情况。
(1)只有p1、p2、p3的状态都变成fulfilled,p的状态才会变成fulfilled,此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数。
(2)只要p1、p2、p3之中有一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。
Generator最大的特点就是可以实现函数的暂停、重启,这个特性非常有利于解决异步操作有两个名词需要注意yield 和 next。
function* gen(x){ try { var y = yield x + 2; } catch (e){ console.log(e); } return y; } var g = gen(1); g.next(); g.throw(‘出错了‘); // 出错了
除此之外,generator函数还有两个小帮手Thunk函数(自动控制Generator函数的流程,接收和交还程序的执行权的一种机制)和co模块(用于Generator函数的自动执行。)
ES7提供了async函数,成为generator函数的语法糖,它主要就是用async来说代替*,用await来代替yield,除此之外,他还带来了一些便利之处:
1.generator函数的调用需要借助next方法或者是co模块,而async和普通函数的调用一样,不需要借助任何函数;
2.co模块约定,yield命令后面只能是Thunk函数或Promise对象,而async函数的await命令后面,可以是Promise对象和原始类型的值
注意的是: await 命令只能用在 async 函数之中,如果用在普通函数,就会报错。
async function dbFuc(db) { let docs = [{}, {}, {}]; // 报错 docs.forEach(function (doc) { await db.post(doc); }); }
上面代码会报错,因为 await 用在普通函数之中了。但是,如果将 forEach 方法的参数改成 async 函数,也有问题。
async function dbFuc(db) { let docs = [{}, {}, {}]; // 可能得到错误结果 docs.forEach(async function (doc) { await db.post(doc); }); }
上面代码可能不会正常工作,原因是这时三个 db.post 操作将是并发执行,也就是同时执行,而不是继发执行。正确的写法是采用 for 循环。
async function dbFuc(db) { let docs = [{}, {}, {}]; for (let doc of docs) { await db.post(doc); } }
如果确实希望多个请求并发执行,可以使用 Promise.all 方法。
async function dbFuc(db) { let docs = [{}, {}, {}]; let promises = docs.map((doc) => db.post(doc)); let results = await Promise.all(promises); console.log(results); } // 或者使用下面的写法 async function dbFuc(db) { let docs = [{}, {}, {}]; let promises = docs.map((doc) => db.post(doc)); let results = []; for (let promise of promises) { results.push(await promise); } console.log(results); }
内容涉及的不是很深入,后续会继续补充和修改,希望大家多多指正
标签:
原文地址:http://www.cnblogs.com/zhuzhenzhen/p/5053271.html