text: /* @flow */
  
  import { _Vue } from '../install'
  import { warn, isError } from './warn'
  
  /**
   * 解析异步路由组件
   * @param {*} matched 
   */
  export function resolveAsyncComponents (matched: Array<RouteRecord>): Function {
    return (to, from, next) => {
      let hasAsync = false
      let pending = 0
      let error = null
  
      flatMapComponents(matched, (def, _, match, key) => {
        // if it's a function and doesn't have cid attached,
        // assume it's an async component resolve function.
        // we are not using Vue's default async resolving mechanism because
        // we want to halt the navigation until the incoming component has been
        // resolved.
        if (typeof def === 'function' && def.cid === undefined) {
          hasAsync = true
          pending++
  
          const resolve = once(resolvedDef => {
            if (isESModule(resolvedDef)) {
              resolvedDef = resolvedDef.default
            }
            // save resolved on async factory in case it's used elsewhere
            def.resolved = typeof resolvedDef === 'function'
              ? resolvedDef
              : _Vue.extend(resolvedDef)
            match.components[key] = resolvedDef
            pending--
            if (pending <= 0) {
              next()
            }
          })
  
          const reject = once(reason => {
            const msg = `Failed to resolve async component ${key}: ${reason}`
            process.env.NODE_ENV !== 'production' && warn(false, msg)
            if (!error) {
              error = isError(reason)
                ? reason
                : new Error(msg)
              next(error)
            }
          })
  
          let res
          try {
            res = def(resolve, reject)
          } catch (e) {
            reject(e)
          }
          if (res) {
            if (typeof res.then === 'function') {
              res.then(resolve, reject)
            } else {
              // new syntax in Vue 2.3
              const comp = res.component
              if (comp && typeof comp.then === 'function') {
                comp.then(resolve, reject)
              }
            }
          }
        }
      })
  
      if (!hasAsync) next()
    }
  }
  
  /**
   * 通过调用flatten方法将matched(records)数组中的每一个routRecord返回的给instances绑定guard守卫的二维数组平整为一维数组
   * 简单讲就是返回一个功能为给instance绑定指定名称守卫的一维数组
   * @param {*} matched 
   * @param {*} fn 
   */
  export function flatMapComponents (
    matched: Array<RouteRecord>,//records
    fn: Function
  ): Array<?Function> {
    return flatten(matched.map(m => { //matched.map 返回 [[fn,fn,fn],[fn,fn,fn]],flatten方法将其平整为[fn,fn,fn,fn,fn,fn]
      return Object.keys(m.components).map(key => fn(
        m.components[key],
        m.instances[key],
        m, key
      ))
    }))
  }
  
  /**
   * 将二维数组平整为一维数组,这里传入的arr参数格式为: [[fn,fn,fn],[fn,fn,fn]] 
   * Array.prototype.concat.apply([], arr)会将其平整为[fn,fn,fn,fn,fn,fn]的形式
   * @param {*} arr 
   */
  export function flatten (arr: Array<any>): Array<any> {
    return Array.prototype.concat.apply([], arr)
  }
  
  const hasSymbol =
    typeof Symbol === 'function' &&
    typeof Symbol.toStringTag === 'symbol'
  
  function isESModule (obj) {
    return obj.__esModule || (hasSymbol && obj[Symbol.toStringTag] === 'Module')
  }
  
  // in Webpack 2, require.ensure now also returns a Promise
  // so the resolve/reject functions may get called an extra time
  // if the user uses an arrow function shorthand that happens to
  // return that Promise.
  function once (fn) {
    let called = false
    return function (...args) {
      if (called) return
      called = true
      return fn.apply(this, args)
    }
  }
  