美文网首页
异步编程

异步编程

作者: zxhnext | 来源:发表于2020-05-19 12:40 被阅读0次

js是单线程原因

● 原因-避免DOM渲染的冲突
● 浏览器需要渲染DOM
● JS可以修改DOM结构
● JS执行的时候,浏览器DOM渲染会暂停
● 两段JS也不能同时执行(都修改DOM就冲突了)
● web worker支持多线程,但是不能访问DOM

1. 回调函数

回调函数是所有异步编程方法的根基

function foo(callback){
  setTimeout(function(){
    callback()
  }, 3000)
}
foo(function(){
  console.log('这就是一个回调函数')
  console.log('调用者定义这个函数,执行者执行这个函数')
  console.log('其实就是调用者告诉执行者异步任务结束后应该做什么')
}

2. 异步调用线程

JS是单线程的,但是浏览器的API是多线程的,例如当setTimeout,当JS执行主线程代码时,倒计时器会有一个单独的线程去计时

来看下图,看下JS在有异步函数时是怎样工作的


image.png

3. Promise

即使Promise中没有异步操作,then中的函数也会进入回调队列排队
Promise本质上也是使用回调函数,定义的异步任务结束后再执行所需要执行的任务

3.1 Promise特点

  1. Promise对象的then方法会返回一个全新的Promise对象
  2. 后面的then方法就是在为上一个then返回的Promise注册回调
  3. 前面then方法中回调函数的返回值会作为后面then方法回调的参数
  4. 如果回调中返回的是Promise,那后面then方法的回调会等待它的结束(后面的then方法相当于该Promise的回调)

promise有值穿透的情况,then 或者 catch 的参数期望是函数,若传入非函数则会发生值穿透。来看如下代码:

Promise.resolve(1)
  .then(2)
  .then(3)
  .then(console.log) // 输出为1

3.2 Promise异常捕获

Promise捕获有两种方式,第一种是在then中传入一个失败函数,这种方式只能捕获到本次Promise异常

then(function success() {}, function fail() {})

第二种是通过catch
catch只能捕获到它的上一个then里的异常,但是因为Promise是链式传递的,不管哪一个then中出现异常,最终都会传递到最后一个then中,因此所有都可以用catch捕获

.then()
.then()
.catch()

3.3 node中注册unhandledRejection方法

process.on('unhandledRejection', (reason, promise) => {
    console.log(reason, promise)
    // reason => Promise 失败原因,一般是一个错误对象
    // promise => 出现异常的 Promise 对象
})

3.4 Promise.resolve()

通过Promise.resolve()可以将第三方Promise对象转为原生Promise

Promise.resolve({
    then: function (onFulfilled, onRejected) {
        onFulfilled('foo')
    }
}).then((value) => {
    console.log(value)
})
var promise = ajax('/api/users.json')
var promise2 = Promise.resolve(promise)
console.log(promise === promise2) // true

3.5 Promise执行顺序问题

catch并不会阻断then的调用

Promise.resolve(2)
.then((res) => {
    console.log(res)
    return Promise.resolve(3)
})
.catch(e => console.log(e))
.then((res) => {
    console.log(res)
})
// 2 3

3.6 使用reduce实现promise.all()效果

const axios = require('axios')

const promises = [
    () => axios('https://api.github.com'),
    () => axios('https://api.github.com/users')
]

const p = promises.reduce((prev, current) => {
    return prev.then(() => current())
}, Promise.resolve())

console.log(p.then(res => console.log(res)))

为了防止有promise出错导致整个promise失败,建议在每个promise后加catch

const promise = Promise.allSettled(urls.map(item => axios(item).catch(e => e)))

4. 宏任务、微任务

回调队列中的任务称为宏任务,新的宏任务需要开启下一轮,而微任务是排在本轮任务末尾,在当前任务结束后立即执行

宏任务:script全部代码、setTimeout、setInterval、setImmediate、I/O、UI Rendering等
微任务:Promise、MutationObserver、process.nextTick

Event Loop: 用于等待和发送消息和事件,负责主线程与其他进程(主要是各种I/O操作)的通信。
具体步骤:

  1. 函数入栈,当Stack中执行到异步任务的时候,就将他丢给WebAPIs,接着执行同步任务,直到Stack为空;
  2. 在此期间WebAPIs完成这个事件,把回调函数放入CallbackQueue中等待;
  3. 当执行栈为空时,检查微任务(microtask)队列,将可执行的微任务全部执行。
  4. Event Loop把Callback Queue中的一个任务放入Stack中,回到第1步。

来看一道题:

setTimeout(() => console.log('A'), 0) // fn1

setTimeout(() => console.log('B'), 1000) // fn2

Promise.resolve()
  .then(() => { // fn3
    setTimeout(() => console.log('C'), 0) // fn4
    setTimeout(() => console.log('D'), 1000) // fn5
    console.log('E')
    Promise.resolve()
      .then(() => console.log('F')) // fn6
  })
  .then(() => console.log('G'))

setTimeout(() => console.log('H'), 0) // fn7

setTimeout(() => console.log('I'), 1000) // fn8

5. Generator

5.1 使用Generator实现异步请求

function* main() {
    try {
        const users = yield ajax('/api/users.json')
        console.log(users)
        const posts = yield ajax('/api/posts.json')
        console.log(posts)
        const urls = yield ajax('/api/urls11.json')
        console.log(urls)
    } catch (e) {
        console.log(e)
    }
}

const g = main()

function handleResult(result) {
    if (result.done) return // 生成器函数结束
    result.value.then(data => handleResult(g.next(data)), error => {
        g.throw(error)
    })
}

handleResult(g.next())

来封装一个统一的co模块

// 封装
function co(generator) {
    const g = generator()

    function handleResult(result) {
        if (result.done) return // 生成器函数结束
        result.value.then(data => handleResult(g.next(data)), error => {
            g.throw(error)
        })
    }
    handleResult(g.next())
}

co(main)

5.2 Async/Await是Generator的语法糖

相关文章

  • ES6学习--异步编程--Generator

    异步编程 : (异步编程的语法目的就是让异步编程更像同步编程) 回调函数利用回调函数实现异步编程本身没有问题, 问...

  • JavaScript异步编程好文摘要

    JavaScript之异步编程简述JavaScript异步编程

  • 认识异步编程

    认识异步编程 本章主要介绍异步编程的概念和作用,Java中异步编程的场景以及不同异步编程场景应使用什么技术实现。 ...

  • part1整理

    函数式编程:JavaScript函数式编程指南 异步编程:异步编程 Promise源码 JavaScript基础知...

  • 论异步编程

    相信本书介绍了CompletableFuture,大家就会认识到什么是异步编程。异步编程的好处。 异步编程最大好处...

  • 读书笔记#Java异步编程实战-上

    Java异步编程实战 chap1 认识异步编程 异步编程概念与作用在使用同步编程方式时,由于每个线程同时只能发起一...

  • Combine 基础知识

    摘自《SwiftUI和Combine编程》---《Combine异步编程》 响应式异步编程模型 将“状态变化”看作...

  • 关于node.js的那些事(四)—异步编程的优势与难点

    有异步I/O,必有异步编程。异步编程有它的优势,也有它的难点,下面我们就这门语言异步编程的优势和难点来展开谈...

  • 异步编程的前世今生

    异步编程的前世今生 1、为什么需要异步编程 异步编程是相对同步编程来说的,开发项目时,开发者总是希望,程序的执行顺...

  • 异步编程控制方法

    javascript 具有的一个特性就是异步编程。异步编程具有的优势本文不做细说,本文主要是总结如何异步编程中出现...

网友评论

      本文标题:异步编程

      本文链接:https://www.haomeiwen.com/subject/uokrohtx.html