乐趣区

关于javascript:JavaScript异步编程

JavaScript 异步编程

从本文你将理解到

  • 同步模式Synchronous or 异步模式Asynchronous
  • 事件循环与队列音讯
  • 异步编程的几种形式
  • Promise 异步计划,宏工作 / 微工作列队
  • Generator 异步计划,Async/Await 语法糖

javascript(js)是单线程从上至下执行

同步和异步模式

同步模式

按程序排队执行,在 Call stack 中压入一个 anonymous 匿名调用, 相当于把全副代码放在匿名函数中去顺次执行
阻塞
同步执行时某条操作比拟耗时会造成卡死

异步模式

不会期待工作完结再执行下一个工作,Callstack | WebAPIs | Event loop | Queue
代码是单线程的,浏览器不是,对于异步会产生一个异步调用线程

事件循环与队列音讯

  • 代码从上至下执行,顺次押入 call stack 主栈中
  • 遇到异步,主栈不会进行期待执行,会将异步工作压入浏览器开拓的另一个线程栈中运行
  • 两线程并行执行
  • 异步线程栈内的异步执行结束后,会将后果从异步栈中弹出至event queue,并期待主栈执行结束
  • 当主栈执行结束,通过 js 外部的 event loop 事件轮询机制将监听到的曾经在 event queue 队列中的异步后果安程序顺次压入主栈中执行
  • console 控制台输入后果

异步编程的几种形式

异步编程计划的根本就是 回调函数

回调函数

相似于一件事, 你明确晓得这件事怎么做,但你不晓得这件事件所依赖的工作什么时候实现, 期待工作实现后调用,回调函数领有回调天堂问题

Promise

CommonJS社区提出了 Promise 标准,回调函数对立解决方案

Pending -> Fufilled -> onFufilled 异步执行胜利
        -> Rejected -> onRejected 异步执行失败

Promise异步计划,宏工作 / 微工作列队

一个根本的 promise 异步

const ps = new Promise(function (resolve,reject) {resolve(100)

  // reject(new Error("promise rejected"))
})

ps.then(function(value){  // 会期待同步代码全副执行完才会执行
  console.log("resolve",value)
},function(err) {console.log("reject",err)
})

console.log("first console")

封装一个 ajax

// 封装一个 ajax
function ajax(url){return new Promise((res,rej)=>{let xhr = new XMLHttpRequest()
    xhr.open("GET",url)
    xhr.onload = function () {if(this.readyState==4 && this.status ==200)
      {res(this.responseText)
      }
      else
      {rej(new Error(xhr.statusText))
      }
    }
    xhr.send()})
}
ajax("/src/api/users.json").then(res=>console.log(res),
        rej=>console.log(rej)
        )

然而下面的 promise 并不能解决回调天堂的问题

  // 回调天堂,减少复杂度
  // ajax("/src/api/users.json").then((function(urls){//   ajax("/src/api/users.json").then((function(urls){//     ajax("/src/api/users.json").then((function(urls){//}))
  //   }))
  // }))

通过 then 办法链式调用

// 通过 promise 的 then 办法的链式调用,使函数扁平化, 防止回调嵌套
//then 办法返回的也是个 promise
// 每一个 then 办法都是在为上一个 then 返回的 promise 对象去增加状态明确过后的回调
//then 办法接管的函数的参数 value  是上一个 then 办法的返回值,如果上一个办法无返回值,则以后 value=null
// 前面的 then 会期待后面的 then(因为是个 promise)完结后执行
ajax("/src/api/users.json")
  .then(value=>{console.log(111);
  }) //=>Promise
  .then(value=>{console.log(222);
    return ajax("/src/api/users.json") // 能够手动增加个 promise 返回对象
  }) //=>Promise
  .then(value=>{console.log(333);
  })

promise 异样解决

//Promise 异样解决
// 抛出一个异样,或者运算异样都会执行失败的回调
function ajaxError(){return new Promise((res,rej)=>{// foo() // 增加一个不存在的办法
    // throw new Error("error") // 手动抛异样
    //...
  })
}

promise 异样回调写法

// 异样回调的写法
// 区别,写在 then 里的 err 函数只能捕捉到第一个 promise 的异样,//catch 能够捕捉到链式调用中的 promise 异样,因为链式调用 promise 异样也会随上一个 then 返回的 promise 传递
//catch 更像是给 promise 链条注册回调
ajaxError().then(function () { },
  function () {}
)
ajaxError().then(res=>{}).then(res=>{}).catch(err=>{})

promise 静态方法

  • promise.resolve()
  • promise.reject()
  • promise.all()
  • promise.race()
//Promise.resolve(参数) 
// 参数是变量则包裹成一个 promise 对象
// 参数是 promise 对象则被原样返回
// 参数是对象,并且这个对象具备和 promise 一样的 then 办法,thenable
//    利用场景是把第三方解决异步插件的 then 办法转化成原生的 promise 对象
Promise.resolve("foo") 
  .then(value=>{console.log(value) //"foo"
  })

new Promise((resolve,reject)=>{resolve("foo")
})

var promise = ajax("/src/api/users.json")
var promise2 = Promise.resolve(promise)
console.log(promise === promise2); //true

Promise.resolve({then(onFulfilled,onRejected){onFulfilled("foo")
  }
}).then(value=>{console.log(value) //"foo"
})
//Promise.reject()
// 疾速创立一个失败的对象
//Promise.all()并行执行
// 承受一个数组,数组里的每个元素是个 promise 对象
// 当数组中 promise 全副执行结束,则并行异步执行完结,返回一个 promise 对象
// 如果其中一个执行失败,那么这个 promise 就会以失败完结
Promise.all([ajax("/src/api/users.json"),
  ajax("/src/api/posts.json")
]).then(vals=>console.log(vals))

// 利用 promise 对象解决 ajax
- urls.json
{
    "urls":"/api/users.json",
    "posts":"/api/users.json"
}

- ajax.ts
ajax("/src/api/urls.json")
  .then(value=>{const urls = Object.values(value)
    const tasks = urls.map(url=>ajax(url))
    return Promise.all(tasks)
  })
  .then(vals=>console.log(vals))
//Promise.race()
// 只有有一个 promise 对象执行实现,则进行执行返回一个 new promise 对象
const request = ajax("/src/api/users.json") 
const settime = new Promise((res,rej)=>{setTimeout(() => {rej(new Error('timeout'))
  }, 800);
})

Promise.race([request,settime])
  .then(val=>console.log(val)) 
  .catch(err=>console.log(err))

// 如果 800ms 内申请到 users.json 那么执行 then 办法
// 如果超时,则执行 catch 办法,(能够应用 network online 限速)

宏工作 / 微工作

// 执行时序,宏工作微工作
console.log("start")
setTimeout(() => {console.log("timeout")
}, 0)
Promise.resolve()
  .then(()=>{console.log('promise1');
  })
  .then(()=>{console.log('promise2');
  })
  .then(()=>{console.log('promise3');
  })
console.log("end");

// 宏工作先执行,执行完结进入回调队列的开端,// 微工作后执行,执行完结在本轮回调队列的开端立刻执行
// 执行先宏后微,弹出先微后宏
// 目前绝大多数异步调用都是作为宏工作执行
//Promise 对象,MutationObserver 对象,node 中 process.nextTick 微工作

Generator 异步计划

//promise chain
// 尽管解决了回调嵌套,然而仍然无奈达到同步代码的可读性
ajax(1000)
  .then(value=>{ajax(2000)
  })
  .then(value=>{ajax(1000)
  })
  .catch(err=>{console.log(err);
  })

//like sync mode 
try{const url = ajax("url.json")
  const url1 = ajax("url1.json")
  const url2 = ajax("url2.json")
}catch(e){console.error(e);   
}

如何像同步代码一样执行呢

function * foo(){console.log("start");
  const res = yield 'foo' //yield 的返回值是下一次 next 执行传入的参数
  console.log(res) 
}

const geneator = foo() // 不会立刻执行,生成器对象
const result = geneator.next()  // 开始执行生成器中的代码
console.log(result) //start  {value:"foo",done:false}
const result1 = geneator.next("hello") // 从上一次 yield 开始持续往下执行,next 可传入参数,console.log(result1); //hello  {value:undefined,done:true}
//done 示意执行器是否执行实现,yield 相似于 return 然而不会完结函数执行,会暂停生成器,直到再次 next()调用

生成器的 throw 办法

function * foo1(){
  try{
    const res = yield 'asdasdasdsa'
    // console.log(res)
  }catch(e){console.log(e)
  }
}

const ge = foo1()
// 执行到第一个 yield 返回对象.value 就是此次 yield 执行后的返回值
console.log(ge.next("第一次传入")) //{value: 'asdasdasdsa', done: false}
ge.throw(new Error("error1")) // 抛出异样,s 会被 catch 捕获并打印

应用生成器执行异步,like async mode

function * main(){
  try{const lastdata = yield ajaxT(1000)
    // console.log(lastdata)
    const lastdata1 = yield ajaxT(2000)
    // console.log(lastdata)
  }catch(e)
  {console.log(e)
  }
}

const g = main()
const result11 = g.next();
(result11.value as Promise<any>).then(data=>{const result22 =  g.next(data);
  if(result22.done) return
  (result22.value as Promise<any>).then(data=>{const result33 = g.next(data)
    if(result33.done) return
  })
})

封装生成器调用办法,递归

// 递归封装一个生成器
function co(geneator){const g = geneator()
  function handleResult(result){if(result.done) return
    result.value.then(data=>{const nextresult = g.next(data)
      handleResult(nextresult)
    },err=>{g.throw(err)
    })
  }
  handleResult(g.next())
}
co(main)
退出移动版