前言
同步:【函数视角】函数调用之后,需要等待函数返回之后再调用下一个函数
异步:【函数视角】函数调用之后,无需等待函数返回就可以调用下一个函数
阻塞:【线程视角】任务调用之后,当前线程被挂起,需要等待被唤醒才可以继续工作
非阻塞:【线程视角】任务调用之后,当前线程不会被挂起,无需等待被唤醒也可以继续工作
串行:【CPU视角】任务之间有依赖关系,每个任务只能由1个CPU核心执行
并行:【CPU视角】任务之间没有依赖关系,多个任务由多个CPU核心执行
es5实现异步串行
难点拆解:
- 核心部分,串行如何实现?串行的本质降维到函数视角,就是一个函数执行完,执行下一个函数;同步的函数还好说,异步的时候需要考虑使用回调来执行下一个函数。那我们就把问题变成:如何实现执行函数之后执行回调?很自然地有两种解法:栈方法循环、递归
- done方法如何实现?done方法的本质是所有任务串行执行完之后再执行done方法。但是需要留意,是先执行函数实体,再返回done方法对象让用户注册回调函数。
show me the code
function log (x, cb) {
console.log(x)
cb && cb()
}
function sleep (v, time, cb) {
setTimeout(() => {
console.log(v)
cb && cb()
}, time)
}
function serialize (tasks) {
let doneCb;
function next () {
let fn = tasks.shift()
if (fn) {
fn(next)
} else {
doneCb && doneCb()
}
}
next();
return {
done: (cb) => {
if (cb) {
doneCb = cb;
}
}
}
}
serialize([
(cb) => log(1, cb),
(cb) => log(2, cb),
(cb) => sleep(3, 3000, cb),
(cb) => log(4, cb)
]).done(() => {
console.log('done')
})
思路总结:
使用递归实现next方法,实现一个任务执行完再执行下一个任务。留个思考题,如果用栈方法又该如何实现呢,还有哪些细节需要处理?
es6实现异步串行
方案一:async/await
难点分析:
- async/await最后会返回promise对象,那如何实现返回done对象呢?里面再实现一个async/await函数即可,外层直接返回done对象。
done方法是否可以用es5方法中一样在判断fn不存在的时候执行呢?不可以,因为遇到await就跳出异步处理了,下面的函数还没有执行,所以此时是拿不到done回调的方法。
function log (x, cb) { console.log(x) } function sleep (v, time) { return new Promise((resolve) => { setTimeout(() => { console.log(v) resolve() }, time) }) } function serialize (tasks) { async function consume () { while (tasks.length) { let fn = tasks.shift() if (fn) { await fn() } } } consume(); return { done: (cb) => { if (cb) { tasks.push(cb) } } } } serialize([ (cb) => log(1, cb), (cb) => log(2, cb), (cb) => sleep(3, 3000, cb), (cb) => log(4, cb) ]).done(() => { console.log('done') })
方案二:Promise
难点分析:
- 如何巧妙使用then方法实现串行?
done方法又如何考虑呢?
serialize([ log(1), log(2), sleep(3), log(4) ]).done(() => { console.log('done') }) // 输出:1 2 3 4 done function log (x) { return () => console.log(x) } function sleep (y) { return () => new Promise((resolve) => { setTimeout(() => { resolve(y) }, 0) }) } function isPromise (obj) { return typeof obj === 'object' && typeof obj.then === 'function' } function serialize (arr) { let promiseResolver = Promise.resolve() arr.forEach(item => { let result promiseResolver = promiseResolver.then(() => { // 同步任务直接执行 result = item(); // 异步任务直接返回,并写一个回调执行console if (isPromise(result)) { return result.then(x => { console.log(x) }) } }) }) return { done: (fn) => { promiseResolver = promiseResolver.then(() => {fn()}); } } }
es5实现异步并行
难点分析:
- 如何实现异步并行?其实就是所有任务都执行一遍,等最迟完成的任务完成了就调用done方法。那如何知道所有任务都执行完了?计算器/状态标记
什么时候调用done方法?那就是计数器得到任务数量,就执行。所有任务的状态标记为执行完毕,就执行。
parallel([ (cb) => log(1, cb), (cb) => sleep(2, 1000, cb), (cb) => log(3, cb), (cb) => sleep(4, 2000, cb), (cb) => log(5, cb) ]).done(() => { console.log('done') }) // 输出:1 3 5 2 4 done // 其中done在2秒后执行 */ function log (x, cb) { console.log(x) cb && cb() } function sleep (v, time, cb) { setTimeout(() => { console.log(v) cb && cb() }, time) } function parallel (tasks) { let doneCb; let count = 0; let tasksLength = tasks.length; function isFinish() { console.log(count, tasksLength) if (count == tasksLength) { doneCb && doneCb() } } function taskCb() { count++; isFinish() } while(tasks.length) { let fn = tasks.shift(); if (fn) { fn(taskCb); } } return { done: (cb) => { if (cb) { doneCb = cb; } } } } parallel([ (cb) => log(1, cb), (cb) => sleep(2, 1000, cb), (cb) => log(3, cb), (cb) => sleep(4, 2000, cb), (cb) => log(5, cb) ]).done(() => { console.log('done') })
es6实现异步并行
function log (x) { return new Promise((resolve) => { console.log(x) resolve() }) } function sleep (v, time) { return new Promise((resolve) => { setTimeout(() => { console.log(v) resolve() }, time) }) } function parallel (tasks) { let doneCb; Promise.all(tasks).then(() => { doneCb && doneCb(); }) return { done: (cb) => { if (cb) { doneCb = cb; } } } } parallel([ log(1), sleep(2, 1000), log(3), sleep(4, 2000), log(5) ]).done(() => { console.log('done') })
Reference
https://cloud.tencent.com/dev...
https://segmentfault.com/a/11...
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。