简单来讲就是一个任务分红多个步骤执行,先执行某一段任务,跳出转而执行其余任务, 等下一段任务准备完成后, 转而回来执行下一段任务前端
像这种类型, 把一个任务分解成多段任务 不连续 执行, 就叫作异步, 连续执行的则叫作同步node
经过拆解一个任务, 分红多段,把第二段任务单独写在第二个函数内,等到须要执行这个任务时, 直接调用这个函数golang
node.js中经常使用的就是如此方法。ajax
fs.readFile('某个文件', function (err, data) { if (err) throw err; console.log(data); });
这是一个错误优先的回调函数(error-first callbacks),这也是Node.js自己的特色之一。 相似golang中的err 错误处理编程
回调带来一些问题, 第一个就是多层嵌套问题, 当一个问题很复杂, 多段不连续, 就会出现地狱嵌套问题数组
fs.readFile('某个文件', function (err, data) { if (err) throw err; fs.writeFile('某个文件',data, function (err, data) { if (err) throw err; fs.readFile('某个文件', function (err, data) { if (err) throw err; console.log("写入的是:",data) }); }); });
没法使用try{}catch(){} 捕获错误
列子:promise
try{ setTimeout(()=>{ callback() throw new Error('抛出错误') },1000) }.catch(err){ console.log('看看是否走到了这里') }
上面的代码是没法走到catch内部的, 因为try{}catch 只能捕获当前任务循环内的任务抛出错误, 而这个回调被存放起来, 直到下一个事件环的时候才会取出, try{}catch实在无能为力app
在node中,已约定回调的第一个参数是抛出的异常。只是用另外的方式来捕获错误。
伪代码异步
let func = function(callback){ try{ setTimeout(()=>{ if(success){ callback(null) }else{ callback(new Error('错误')) } },1000) }catch(e){ console.log('捕获错误',e); } }
一般在前端操做的通常是经过addeventLisener监听各类事件,好比键盘事件 鼠标事件等等,async
document.addeventListener('click',function(e){ console.log(e.target) },false)
一般把须要执行的任务先暂存起来, 等达到条件或者发布的时候一一拿出来执行
class Task{ construct(){ this.tasks = {} } publish(event){ this.tasks[event].forEach(fn=>fn()) } subscribe(event,eventTask){ this.tasks[event] = this.tasks[event] ? this.tasks[event] : [] this.tasks[event].push(eventTask) } } let task = new Task() task.subscribe('eat',function(){console.log('吃午餐')}) task.subscribe('eat',function(){console.log('吃晚饭')}) task.publish('eat')
Promise/Deferred模式
生成器Generators/ yield
function* foo () { var index = 0; while (index < 2) { yield index++; //暂停函数执行,并执行yield后的操做 } } var bar = foo(); // 返回的实际上是一个迭代器 console.log(bar.next()); // { value: 0, done: false } console.log(bar.next()); // { value: 1, done: false } console.log(bar.next()); // { value: undefined, done: true }
yield是一个表达式, 后面紧跟着的表达式是next()的返回结果的value,而若是想给yield传递参数,好比a=yield 1,给a传递值 则要next(value))
// 例子: function* foo () { a = yield 1 console.log(a) // 10 } var bar = foo(); // 返回的实际上是一个迭代器 console.log(bar.next()); // { value: 1, done: false } console.log(bar.next(10)); // { value: undefined, done: true }
能够理解为yield有两步操做,第一个next弹出值,第二个next接收值而且执行一下段语句,直到下一个yield弹出值为止
function* iterArr(arr) { //迭代器返回一个迭代器对象 if (Array.isArray(arr)) { // 内节点 for(let i=0; i < arr.length; i++) { yield* iterArr(arr[i]); // (*)递归 } } else { // 离开 yield arr; } } var arr = [ 'a', ['b',[ 'c', ['d', 'e']]]]; var gen = iterArr(arr); arr = [...gen];
function* main(){ try{ let result = yield foo() console.log(result) }catch(e){ console.log(e) } } let it = main() function foo(params,url){ $.ajax('www.baidu.com', function(err,data){ if(err){ it.throw(err) }else{ it.next(data) } } } it.next() // {value:undefind,done:false}
Promise 对象用于表示一个异步操做的最终完成 (或失败), 及其结果值.
他有三个状态 分别是pending resolved 以及rejected
一旦发生状态改变, 就不可再更改了 每次then都另外建立一个promise对象
// 伪代码 class Promise{ construct(executor){ let self = this // 一个 Promise有如下几种状态: // pending: 初始状态,既不是成功,也不是失败状态。 // fulfilled: 意味着操做成功完成。 // rejected: 意味着操做失败。 this.status = 'pending' this.res = undefined // 存成功以后的值 this.err = undefined // 存失败以后的值 this.onFulfilledCallback = [] this.onRejectedCallback = [] function resolve(res){ if(self.status === 'pending'){ self.status = 'resolved' self.res = res onFulfilledCallback.forEach(fn=>fn()) } } function reject(err){ if(self.status === 'pending'){ self.status = 'rejected' self.err = err onRejectedCallback.forEach(fn=>fn()) } } // executor是带有 resolve 和 reject 两个参数的函数 。Promise构造函数执行时当即调用executor 函数, resolve 和 reject 两个函数做为参数传递给executor(executor 函数在Promise构造函数返回所建promise实例对象前被调用)。resolve 和 reject 函数被调用时,分别将promise的状态改成fulfilled(完成)或rejected(失败)。executor 内部一般会执行一些异步操做,一旦异步操做执行完毕(可能成功/失败),要么调用resolve函数来将promise状态改为fulfilled,要么调用reject 函数将promise的状态改成rejected。若是在executor函数中抛出一个错误,那么该promise 状态为rejected。executor函数的返回值被忽略。 executor(resolve,reject) } then(onFulfilled,onRejected){ let self = this return new Promise((resolve,reject)=>{ if(self.status === 'resolved'){ let x = onFulfilled(self.res) // 拿到onFulfilled的执行结果 注意:这里执行的是Promise.resolve() 同步代码 // 而后把x传递给下一个then resolve(x) } if(self.status === 'rejected'){ let x = onRejected(self.res) // 拿到onFulfilled的执行结果 注意:这里执行的是Promise.resolve() 同步代码 // 而后把x传递给下一个then reject(x) } if(self.status === 'pending'){ self.onFulfilledCallback.push(function(){ let x = onFulfilled(self.res) // 这里的self.res 是上一个new Promise上的值 此时的onFUlfilled 至关于 fn(){let x = onFulfilled} resolve(x) }) self.onRejectedCallback.push(function(){ let x = onRejected(self.res) // 这里的self.res 是上一个new Promise上的值 此时的onFUlfilled 至关于 fn(){let x = onFulfilled} reject(x) }) } }) } }
function request(){ return new Promise((resolve,reject)=>{ setTimeout(()=>{ resolve({data:'得到数据'}) },1000) }) } request().then(data=>{ console.log(data) })
连接:Promise
function foo(x,y) { return request( "http://some.url.1/?x=" + x + "&y=" + y ); } function *main() { try { var text = yield foo( 11, 31 ); console.log( text ); } catch (err) { console.error( err ); } } let it = main() it.next()
var text = yield foo( 11, 31 )跟async await 是否是很像?
生成器能够 yield 一个 promise,而后这个 promise 能够被绑定,用其完成值来恢复这个生成器的运行。
// 伪代码 function run(gen){ // 参数是一个gen函数 let it = gen.apply(this) return Promise.resolve().then((value)=>{ let next = it.next(value) return (function nextHandle(next){ if(next.done === true){ return next.value }else{ return Promise.resolve(next.value).then(nextHandle) // 递归 } })(next) }) } function* main(){ function *main() { try { var text = yield foo( 11, 31 ); console.log( text ); } catch (err) { console.error( err ); } } } run(main).then((data)=>{ // do something })
AsyncFunction 构造函数用来建立新的 异步函数 对象,JavaScript 中每一个异步函数都是 AsyncFunction 的对象。
注意,AsyncFunction 并非一个全局对象,须要经过下面的方法来获取
Object.getPrototypeOf(async function(){}).constructor
语法:new AsyncFunction([arg1[, arg2[, ...argN]],] functionBody)
var AsyncFunction = Object.getPrototypeOf(async function(){}).constructor; var a = new AsyncFunction('a', 'b', 'return await resolveAfter2Seconds(a) + await resolveAfter2Seconds(b);'); a(10, 20).then(v => { console.log(v); // 4 秒后打印 30 });
可是上面这种方式不高效 由于经过字面量建立的异步函数是与其余代码一块儿被解释器解析的,而new这种方式的函数体是单独解析的。
async a(a,b){ return await resolveAfter2Seconds(a) + await resolveAfter2Seconds(b) }
await 表达式会暂停当前 async function 的执行,等待 Promise 处理完成。若 Promise 正常处理(fulfilled),其回调的resolve函数参数做为 await 表达式的值,继续执行 async function。
若 Promise 处理异常(rejected),await 表达式会把 Promise 的异常缘由抛出。
另外,若是 await 操做符后的表达式的值不是一个 Promise,则返回该值自己。
function resolveAfter2Seconds(x) { return new Promise(resolve => { setTimeout(() => { resolve(x); }, 2000); }); } async function f1() { var x = await resolveAfter2Seconds(10); console.log(x); // 10 } f1();