异步编程 - 如何控制好异步过程

参考资料 ​​阮一峰 Javascript异步编程的4种方法​​)

  • JS的执行环境是单线程(Single thread)
  • I/O处理需要回调函数异步处理 (异步I/O)
  • 前端异步IO可以消除UI阻塞,提高用户体验
  • 而放在后端则可以提高CPU和内存里利用率

串联异步处理

异步操作队列化,按照期望的顺序执行。

Callback

回调地域太可怕

const logTime = (name) => {
console.log(`Log....${name} ` + new Date().toLocaleTimeString())
}

exports.callback = () => {
setTimeout(() => {
logTime('callback 1')
setTimeout(() => {
logTime('callback 2')
}, 100)
}, 100)
}

测试代码

test('callback', done => {
callback()
// 延时4s结束
setTimeout(done, 1000)
})

Promise

The Promise object is used for asynchronous computations. A Promise represents a single asynchronous operation that hasn't completed yet, but is expected in the future.

译文:Promise对象用于异步操作,它表示一个尚未完成且预计在未来完成的异步操作。

说白了就是一个异步执行的状态机,异步执行的承诺。

const promise = (name, delay = 100) => new Promise(resolve => {
setTimeout(() => {
logTime(name)
resolve()
}, delay)
})

exports.promise = () => {

promise('Promise1')
.then(promise('Promise2'))
.then(promise('Promise3'))
.then(promise('Promise4'))
}

Gennerator

ES6 新引入了 Generator 函数,可以通过 yield 关键字,把函数的执行流挂起,为改变执行流程提供了可能,从而为异步编程提供解决方案。 基本

  • function -> function* 称为Gennerator函数
  • 函数内部有 yield 表达式。
function* func() {
console.log("one");
yield '1';
console.log("two");
yield '2';
console.log("three");
return '3';
}

const f = func()
f.next();
// one
// {value: "1", done: false}
f.next();
// two
// {value: "2", done: false}
f.next();
// three
// {value: "3", done: true}
f.next();
// {value: undefined, done: true}

// 或者通过迭代器
for (const [key,value] of func()) {
console.log(`${key}: ${value}`);
}
逻辑代码
let co = function (gen, name) {
var it = gen(name)
var ret = it.next()
ret.value.then(function (res) {
it.next(res)
})
}
exports.generator = () => {
const generator = function* (name) {
yield promise(name + 1)
yield promise(name + 2)
yield promise(name + 3)
yield promise(name + 4)
}
let co = generator => {
if (it = generator.next().value) {
it.then(res => {
co(generator)
})
} else {
return
}
}
co(generator('Co-Generator'))
}

async/await

async/await是es7推出的一套关于异步的终极解决方案

  • 任何一个await语句后面的 Promise 对象变为reject状态,那么整个async函数都会中断执行。
  • async函数返回的 Promise 对象,必须等到内部所有await命令后面的 Promise 对象执行完,才会发生状态改变,除非遇到return语句或者抛出错误。也就是说,只有async函数内部的异步操作执行完,才会执行then方法指定的回调函数。
exports.asyncAwait = async () => {
await promise('Async/Await1')
await promise('Async/Await2')
await promise('Async/Await3')
await promise('Async/Await4')
}

事件监听方式处理

采用事件驱动模式。任务的执行不取决于代码的顺序,而取决于某个事件是否发生。

exports.event = async () => {
const asyncFun = name => event => {
setTimeout(() => {
logTime(name)
event.emit('end')
}, 100)
return event
}

const ary = [
asyncFun('event1'),
asyncFun('event2'),
asyncFun('event3')
]

const { EventEmitter } = require('events')
const event = new EventEmitter()
let i = 0
event.on('end', () => i < ary.length && ary[i++](event))
event.emit('end')

}

eventEmmiter

const promise = (name, delay = 100) => new Promise(resolve => {
setTimeout(() => {
logTime(name)
resolve()
}, delay)
})

exports.promise = () => {

promise('Promise1')
.then(promise('Promise2'))
.then(promise('Promise3'))
.then(promise('Promise4'))
}

扩展阅读eventEimitter源码解析 / 订阅发布机制

class EventEmitter {
constructor() {
this.handler = {};
}
on(eventName, callback) {
if (!this.handles) {
this.handles = {};
}
if (!this.handles[eventName]) {
this.handles[eventName] = [];
}
this.handles[eventName].push(callback);
}
emit(eventName, ...arg) {
if (this.handles[eventName]) {
for (var i = 0; i < this.handles[eventName].length; i++) {
this.handles[eventName][i](...arg);
}
}

}
}

const event = new EventEmitter();
event.on('some_event', num => {
console.log('some_event 事件触发:'+num);
});
let num = 0
setInterval(() => {
event.emit('some_event' , num ++ );
}, 1000);

RxJS

对于事件流的控制 -- 近期推出 --