为什么使用异步编程
JavaScript为单线程程序,若其中某个函数或方法占用时间过长,阻塞了进程,导致用户一直需要等待当前函数执行完成之后才能进行下一步操作。
这严重影响了用户体验及程序运行效率。
为了解决这一问题,所以采用异步执行。
异步解决方案
异步进化史:回调函数、Promise、Generator、ansyc/await
回调函数 – 所有异步编程方案的根基
由调用者定义,交由执行者执行的函数,被称为回调函数。
缺点
当有多层回调嵌套时,产生回调地狱。不利于代码的可读性及可维护性。
Promise – 更优雅的异步编程方案
promise状态:
Pending(等待),Fulfilled(成功),Rejected(失败)。
基本用法
1 | const promise = new Promise(function(resolve,reject){ |
使用案例
1 | //promise 封装ajax |
链式调用
嵌套使用Promoise是使用promise最大的误区。
Promise链式调用,避免回调嵌套。
promise的then方法中,若是返回一个Promise,则下一个then方法则是为上一条then方法执行回调。
若返回的是一个普通的值,则这个值作为下一个then方法中的接收值。
若未返回任何值,则默认返回undefied
- Promise 的then方法会返回一个全新的Promise对象
- 后面的then方法是在为上一个then方法中返回的Promise注册回调
- 前面的then方法中回调函数的返回值会作为面then方法回调的参数
- 如果回调中返回的是Promise,那后面的then方法的回调会等待Promise结束后执行
异常处理
手动捕获异常
then方法接收onFulfilled方法和onRejected方法,当Promise函数执行出现异常时,会执行onRejected回调函数,进行异常处理。
catch方法可以接受onRejected方法,当Promise函数执行出现异常时,会执行onRejected回调函数,进行异常处理。
区别
通过catch方法会捕获前面所有Promise的异常
then方法中的onRejected方法只能捕获当前Promise的异常
unhandledrejection
全局注册unhandledrejection方法,用于捕获没有手动捕获的异常
1 | window.addEventListener('unhandledrejection',event=>{ |
Promise 静态方法
Promise.resolve()
快速将一个值转化为Promise对象
若接收参数为一个Promise对象,则Promise对象会被原样返回
若接收参数为一个对象,且对象中的then方法为有onFulfilled和onrejected回调的函数,则实现了一个thenable的接口,对象可以像Promise一样被then执行。
1 | Promise.resolve('foo') |
1 | //thenable对象 |
Promose.reject()
快速创建一个一定会失败的Promise对象
无论传入什么参数,都会作为Promise失败的原因。
Promise 并行执行
Promise.all()
将多个Promise合并为一个Promise统一管理
接收一个数组,数组中元素为Promise对象。
所有Promise执行完成后,返回一个Promise对象,Promise对象中包含一个数组,数组中记录着每个Promise执行的结果。
若Promise执行中有任务失败,则整个Promise结束。
Promise.race()
将多个Promise合并为一个Promise
多个promise对象中有一个执行完成后,新的Promise执行完成。
Promise执行时序
promise的then方法是异步微任务,要等所有同步任务执行完成之后,才会执行。
Generator — 生成器函数
调用生成器函数时,不会执行函数,而是回返回一个生成器对象,手动调用生成器对象的next方法,才会执行函数。
在函数内部,可以使用yield
关键词返回一个值,并可以通过next方法返回值中的value拿到该返回值。
yield
方法不会像return语句一样结束函数的执行,只是暂停函数的执行。下次执行next会继续执行函数。
若使用next方法时传入值,则可以在yeild中接收这个值。
在生成器函数中,可以使用try/catch捕获生成器throw的异常。
异步方案
1 | //ajax异步方法 |
1 | //普通执行处理函数 |
1 | //递归执行函数处理 |
封装执生成器函数执行器
1 | function co(generator){ |