Javascript:异步编程

异步编程

回调函数 的方式,使用回调函数的方式有一个缺点是,多个回调函数嵌套的时候会造成回调函数地狱,上下两层的回调函数间的代码耦合度太高,不利于代码的可维护。

Promise 的方式,使用 Promise 的方式可以将嵌套的回调函数作为链式调用。但是使用这种方法,有时会造成多个 then 的链式调用,可能会造成代码的语义不够明确。

generator 的方式,它可以在函数的执行过程中,将函数的执行权转移出去,在函数外部还可以将执行权转移回来(让出线程)。当遇到异步函数执行的时候,将函数执行权转移出去,当异步函数执行完毕时再将执行权给转移回来。因此在 generator 内部对于异步操作的方式,可以以同步的顺序来书写。使用这种方式需要考虑的问题是何时将函数的控制权转移回来,因此需要有一个自动执行 generator 的机制,比如说 co 模块等方式来实现 generator 的自动执行。

async 函数 的方式,async 函数是 generator + promise 实现的一个自动执行的语法糖,它内部自带执行器,当函数内部执行到一个 await 语句的时候,如果语句返回一个 promise 对象,那么函数将会等待 promise 对象的状态变为 resolve 后再继续向下执行。因此可以将异步逻辑,转化为同步的顺序来书写,并且这个函数可以自动执行。

回调地狱的弊端

  1. 嵌套函数存在耦合性,一旦有所改动,就会牵一发而动全身
  2. 嵌套函数一多,就很难处理错误,不能使用 try catch 捕获错误,不能直接 return

Promise

Promise是异步编程的一种解决方案,它是一个对象,可以获取异步操作的消息,他避免了传统回调函数地狱回调问题,简单说就是一个容器,里面可以获取异步操作的消息,保存着异步操作结果。

状态Pending 进行中、Resolved 已完成、Rejected 已拒绝

过程

  • pending -> fulfilled : Resolved(已完成)
  • pending -> rejected:Rejected(已拒绝)

特点

  • 在构造 Promise 的时候,构造函数内部的代码是立即执行的(同步)
  • 一旦状态改变就不会再变

优点:解决了回调地狱

缺点

  • 无法取消Promise
  • 不设置回调函数,Promise内部抛出的错误
  • 当处于pending状态时,无法得知目前进展到哪一个阶段

创建Promise对象

 const promise = new Promise(function(resolve, reject) {
   // ... some code
   if (/* 异步操作成功 */){
     resolve(value);
  } else {
     reject(error);
  }
 });
 ​
 // promise.then:接受两个参数:成功的回调、失败的回调
 promise.then(function(value) {
   // success
 }, function(error) {
   // failure
 });
 ​
 //promise.catch:接受失败的回调
 promise.catch(function(error) {
   // failure
 });
 ​
 //promise.finally:无论成功失败都最终执行
 promise.catch(function(error) {
   // some end function
 });
 ​
  • Promise.resolve promise = Promise.resolve(value)
     //相当于
     promise = new Promise((resolve) => resolve(value));
  • Promise.reject promise = Promise.reject(value)
     //相当于
     promise = new Promise((resolve,reject) => reject(value));

Promise方法

Promise常用的方法:all()、race()、any()。下面就来看一下这些方法。

  • Promise.all只有当该数组中的所有Promise完成后才会由pendding状态变为resolve执行then里面的回调函数,若数组中有任意一个promise被拒绝则会执行失败回调,catch方法会捕获到首个被执行的 reject函数 Promise.all([promise1,promise2,…]).then(res => {
      //res:一个数组,这个数组按顺序保存着每一个promise对象resolve执行时的值
     }).catch(err => {
       //err:首个被执行的reject的结果
     })
  • Promise.race当promise数组中任意一个promise被拒绝或者成功,则会采用第一个promise作为他的返回值。若为成功的执行then,若失败则执行catch。 Promise.any([promise1,promise2,…]).then(res => {
      //res:返回第一个结果(如果成功)
     }).catch(err => {
       //err:返回第一个结果(如果失败)
     })
  • Promise.any当传入的promise数组中有任意一个完成时就会终止,会忽略到所有被拒绝掉的promise,直到第一个promise完成。若传入所有的promise被拒绝则会执行拒绝回调。 Promise.race([promise1,promise2,…]).then(res => {
      //res:返回第一个成功结果
     }).catch(err => {
       //err:AggregateError: All promises were rejected(所有结果都失败了)
     })

Promise 实现

 const PENDING = "pending" //待解决的
 const FULFILLED = "fulfilled" //已实现
 const REJECTED = "rejected" //已失败
 ​
 class MyPromise {
     state = PENDING
     value = undefined
     callbacks = []
 ​
     /*
    * 把MyPromise的_resolve和_reject绑定了本MyPromise的this的方法传给参数
    * new MyPromise((resolve,reject)=>{
    *   //executor要执行的函数
    * })
    * */
     constructor(executor) {
         try {
             //(resolve,reject)=>{some function}
             executor(this._resolve.bind(this), this._reject.bind(this))
        } catch (error) {
             this._reject(error)
        }
    }
 ​
     /*
    * 用户觉得成功后,来执行resolve(value)
    * */
     _resolve(value) {
         if (this.state !== PENDING) return;//确保Promise的状态确定不变性
         if (typeof value === 'object' && value instanceof MyPromise) {
             //传来的是MyPromise实例:如果它成功了则让它执行本实例的_resolve(),否则_reject()
             /*
            * resolve(new MyPromise((resolve,reject)=>{   }))
            * */
             value.then(this._resolve.bind(this), this._reject.bind(this))
        } else {
             //把本MyPromise实例置为成功态,并触发后续回调(之前绑定在本实例的then(回调函数))
             this.state = FULFILLED;
             this.value = value
             this.callbacks.forEach(callback => this._handle(callback))//交给_handle去调用callback
        }
    }
 ​
     /*
    * 用户觉得失败后,来执行reject(reason)
    * */
     _reject(reason) {
         if (this.state !== PENDING) return;//确保Promise的状态确定不变性
         if (typeof reason === 'object' && reason instanceof MyPromise) {
             //传来的是MyPromise实例:如果它成功了则让它执行本实例的_resolve(),否则_reject()
             /*
            * reject(new MyPromise((resolve,reject)=>{   }))
            * */
             reason.then(this._resolve.bind(this), this._reject.bind(this));
        } else {
             this.state = REJECTED;
             this.value = reason
             this.callbacks.forEach(callback => this._handle(callback))//交给_handle去调用callback
        }
    }
 ​
     /*
    * 回调函数处理器
    * _handle(callback)
    * 参数的 callback = {onFulfilled, onRejected, resolve, reject}
    * 参数的 callback = {.then(res=>{ onFulfilled执行内容 }), .catch(err=>{onRejected执行内容}), resolve, reject}
    * _handle({即将要执行的成功回调, 即将要执行的失败回调, 下一个MyPromise实例的成功调用, 下一个MyPromise实例的失败调用})
    * */
     _handle(callback) {
         if (this.state === PENDING) {
             //还没执行完,先放到回调队列
             this.callbacks.push(callback)
        } else {
             try {
                 if (this.state === FULFILLED) {
                     //成功态调用成功回调,把本MyPromise的resolve执行时传过来的参数传给onFulfilled
                     let res = (typeof callback.onFulfilled) === 'function' ? callback.onFulfilled(this.value) : this.value
                     // 如果onFulfilled是函数则把他的结果返回给下一个MyPromise实例,如果不是函数就传 本MyPromise的resolve执行时传过来的参数
                     callback.resolve(res)
                } else if (this.state === REJECTED) {
                     //失败态调用失败回调
                     if (typeof callback.onRejected === 'function') {
                         //有处理Rejected的回调函数则处理,并返回它的返回值,把下一个MyPromise实例置为成功态
                         let res = callback.onRejected(this.value)
                         callback.resolve(res)
                    } else {
                         //没有则说明还没处理,继续报错下去,把下一个MyPromise实例置为失败态
                         callback.reject(this.value)
                    }
                }
            } catch (error) {
                 //失败了,去调用下一个MyPromise的reject方法通知失败了
                 callback.reject(error)
            }
        }
    }
 ​
     /*
    * promise2 = promise.then(res=>{
    *   //成功的回调
    * },err=>{
    *   //失败的回调
    * })
    * 返回了新的MyPromise实例(promise2),并且马上触发本实例(promise)的_handle
    * */
     then(onFulfilled, onRejected) {
         return new MyPromise((resolve, reject) => {
             this._handle({onFulfilled, onRejected, resolve, reject})
        })
    }
 ​
     //同上,与then的第二个回调一样
     catch(onError) {
         return this.then(null, onError)
    }
 ​
     //其实就是通过本实例的.then()生成新的MyPromise实例,但无论onDone回调返回什么值,是否报错都无法获取或捕获,只负责把当前状态和值转给下一个MyPromise实例
     finally(onDone) {
         if (typeof onDone !== 'function') {
             return this.then()
        } else {
             return this.then(
                 value => MyPromise.resolve(onDone()).then(() => value),
                 reason => MyPromise.resolve(onDone()).then(() => {
                     throw reason
                })
            );
        }
    }
 ​
     //静态函数区域
     //MyPromise.resolve()
     static resolve(value) {
         if (typeof value === 'object' && value instanceof MyPromise) {
             //如果已经是MyPromise实例就不用包装了
             return value
        } else if (value && typeof value === 'object' && typeof value.then == 'function') {
             //如果是对象类型,且有自带的then方法 返回一个新的MyPromise实例并马上置为成功态且
             /*
            * MyPromise.resolve({
            *   then(resolve,reject){
            *       ...,
            *       resolve(someVal) //或者reject(someVal)
            *   }
            * })
            * */
             return new MyPromise((resolve,reject) => value.then(resolve,reject))
        } else {
             //最常用的是只传了值,就直接返回新的MyPromise实例并置为成功态且把它传过去
             return new MyPromise(resolve => resolve(value))
        }
    }
 ​
     //MyPromise.reject()
     static reject(reason) {
         return new MyPromise((resolve,reject) => reject(reason))
    }
 ​
  //MyPromise.all()
     static all(promiseList) {
         return new MyPromise((resolve, reject) => {
             let res = []
             let fulfilledCount = 0
             promiseList.forEach((promise, index) => {
                 MyPromise.resolve(promise).then(result => {
                     res[index] = result
                     fulfilledCount++
                     if (onFulfilledNum === promiseList.length) resolve(res)
                }).catch(err => {
                     res[index] = err
                     reject(err)
                })
            })
        })
    }
 ​
  //MyPromise.race()
     static race(promiseList) {
         return new MyPromise((resolve, reject) => {
             promiseList.forEach((promise, index) => {
                 MyPromise.resolve(promise).then(result => {
                     resolve(result)
                }).catch(err => {
                     reject(err)
                })
            })
        })
    }
 }

Generator函数

Generator函数:是ES6提供的解决异步编程的方案之一,它是一个状态机,内部封装了不同状态的数据,用来生成遍历器对象,可暂停函数(惰性求值)yield暂停next 方法可启动。每次返回的是yield后的表达式结果。

特点:

  • function与函数名之间有一个星号
  • 内部用yield表达式来定义不同的状态
  • generator函数返回指针对象(iterator),而不会执行函数内部逻辑
  • 调用next方法函数内部逻辑开始执行,遇到yield表达式停止, 返回 { value: yield 后的表达式结果 }
  • 再次调用next方法会从上一次停止时的yield处开始,直到最后
  • yield语句返回结果通常为undefined,当调用next方法时传参内容会作为启动时yield语句的返回值。
 //function* myGenerator()
 //function * myGenerator()
 //function *myGenerator() 都可以
 function* myGenerator() {
   console.log('开始执行');
   let result = yield 'Hello';
   console.log(result);
   console.log('暂停后再次执行');
   yield 'generator';
   console.log('遍历完毕');
   return '返回结果';
 }
 console.log('Generator函数:');
 let MG = myGenerator(); // 返回的是指针对象
 console.log(MG);//myGenerator {<suspended>}
 console.log(MG.next());//开始执行 {value: 'Hello', done: false}
 console.log(MG.next('aaaaaaaaaaa'));//aaaaaaaaaaa 暂停后再次执行 {value: 'generator', done: false}
 console.log(MG.next());//遍历完毕 {value: '返回结果', done: true}
 console.log(MG.next());//{value: undefined, done: true}
 console.log(MG.next());//undefined 最后一个用return返回,所以是undefined,如果最后是用yield,就一直返回{value: undefined, done: true}
 ​
 let o = {}
 o[Symbol.iterator] = function* myFun(){
   yield 1;
   yield 2;
   yield 3;
 }
 for(let item of o){
  console.log(item)
 }
 //1 2 3

async/await

async/await其实是 Generator 的语法糖,它能实现的效果都能用then链来实现,它是为优化then链而开发出来的。async函数返回的是一个 Promise 对象,如果在函数中 return 一个直接量,会通过Promise.resolve()封装成 Promise 对象,之后可通过then来把它拿出来。

 async function testAsy(){
    return 'hello world'
 }
 let result = testAsy()
 console.log(result) // Promise {<fulfilled>: 'hello world'}
 result.then(v=>{
     console.log(v)   // hello world
 })

await:await暂停当前async的执行,让出当前async函数线程,等待一个 promise 完成 或者 其它值

  • 如果它等到的不是一个 Promise 对象,那 await 表达式的运算结果就是它等到的东西。
  • 如果它等到的是一个 Promise 对象,await 就忙起来了,它会阻塞后面的代码,等着 Promise 对象 resolve,然后得到 resolve 的值,作为 await 表达式的运算结果。
 async function testAsync() {
     return new Promise((resolve,reject)=>{
       console.log('promise start')
  setTimeout(()=>resolve('promise ok'),1000)
  })
 }
 async function test() {
  console.log('async start');
     const v1 = await "OK";
     console.log(v1); //OK
     const v2 = await testAsync();
     console.log(v2); //promise ok
 }
 test();
 console.log('script end')
 //输出顺序:async start、script end、OK、promise start、promise ok

优点

  • 优化then链:代码读起来更加同步,Promise虽然摆脱了回调地狱,但是then的链式调⽤也会带来额外的阅读负担
  • 中间值传递写法优雅:Promise传递中间值⾮常麻烦,⽽async/await⼏乎是同步的写法,⾮常优雅
  • 错误处理友好:async/await可以⽤成熟的try/catch,Promise的错误捕获⾮常冗余
  • 调试友好:Promise的调试很差,由于没有代码块,你不能在⼀个返回表达式的箭头函数中设置断点,如果你在⼀个.then代码块中使⽤调试器的步进(step-over)功能,调试器并不会进⼊后续的.then代码块,因为调试器只能跟踪同步代码的每⼀步。

async/await 如何捕获异常

 async function fn(){
     try{
         let a = await Promise.reject('error')
    }catch(error){
         console.log(error)
    }
 }

发表评论

您的电子邮箱地址不会被公开。