概述

同步模式

异步模式

回调函数

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

回调函数其实就是 todoList,就是你想要做的事

调用者,执行者,由调用者定义,交给执行者执行的函数就是回调函数

举个🌰:

女朋友 想吃滑蛋叉烧饭,然后把做菜的步骤发给男朋友之后,就去看电视剧了,然后男朋友就按照女朋友给的步骤,就去做菜了,做完菜后端给女朋友吃。整个过程,女朋友就是调用者,男朋友就是执行者,整个过程就是一个回调函数,因为女朋友并没有在等待,就去看电视剧了!!

Promise的概述

Promise的基本用法


// Promise的基本示例

// 需要传入一个函数作为参数,这个函数会在构造Promise的过程中,**同步**执行!!

// 这个函数内部,接收两个参数,resolve和reject,这两个参数都是函数,

// 因为Promise的状态一旦确定过后,就不会再被修改,所以二者只会执行其中之一

const promise = new Promise(function (resolve, reject) {

    // 用于兑现承诺,下面的二者方法只能二选一

    resolve(100) // 承诺达成

    // reject(new Error('Promise rejected')) // 承诺失败

})

// 使用then方法分别指定onFullfilled回调函数,和onReject回调函数

// 这里的promise的回调会先进入回调队列进行排队,等主执行栈里的全部同步代码执行完毕之后才会执行

promise.then(function(value) {

    // 承诺成功的回调

    console.log('resolved', value)

}, function(error) {

    // 承诺失败的回调

    console.log('rejected', error)

})

console.log('end')

// 打印结果

// end

// resolved 100

Promise的使用案例


// Promise 方式的 AJAX

function ajax(url) {

    // 对外返回一个Promise对象,即对外做出一个承诺

    return new Promise(function(resolve, reject) {

        var xhr = new XMLHttpRequest()

        xhr.open('GET', url)

        xhr.responseType = 'json'

        xhr.onload = function() {

            if (this.status === 200) {

                resolve(this.response)

            } else {

                reject(new Error(this.statusText))

            }

        }

        xhr.send()

    })

}

Promise的常见误区

  • 嵌套使用的方式是使用Promise的最常见的误区

Promise的链式调用

.then方法返回的是一个全新的Promise

每一个then方法都是为了上一个then返回的Promise对象添加状态明确过后的回调

  • Promise对象的then方法会返回一个全新的Promise
  • 后面的then方法就是在为上一个then返回的Promise注册回调 Question:这里的注册回调是插入到回调队列里吗???
  • 前面的then方法中回调函数的返回值会作为后面的then方法回调的参数
  • 如果回调中返回的是Promise,那后面then方法的回调会等待它的结束

Promise异常处理

onRejected在Promise失败了,或者是出现异常时都会被调用,也可以使用catch方法进行对onRejected方法的注册


// then方法传入两个参,一个onFullfilled,一个onRejected

ajax('/user.json').then(function onFullfilled(value) {

    console.log('onFullfilled', value)

}, function onRejected(err) {

    console.log('onRejected', value)

})

// 这里与上面等价,其实catch方法就是then方法的别名

ajax('/user.json')

    .then(function onFullfilled(value) {

        console.log('onFullfilled', value)

    }) // 返回一个新的Promise,下面的catch是捕获返回新Promise的错误

    .catch( function onRejected(err) {

        console.log('onRejected', value)

    })

// 等价于 then 方法第一个参数传的undefined

// .then(undefined, function onRejected(err) {

//    console.log('onRejected', err)

// })

区别:

  1. PromiseA的then方法传递onRejectd函数参数时,then方法里面的 onFullfilled函数参数里面返回的是一个新的PromiseB时,onRejected方法是没办法捕获到的,因为onRejected函数是对当前PromiseA做异常处理捕获
  2. 而用catch方法进行对异常处理的捕获是可以解决1上面的问题,那是因为,catch是then方法的别名,也就是执行了另一个then方法,前面说的then方法的是前一个PromiseA返回的新的PromiseB进行注册回调,也就是说能对新的PromiseB进行异常处理

Promise的静态方法

  • Promise.resolve() 快速的把一个值转换为一个Promise对象
  • Promise.reject() 快速的创建一个一定是失败的Promise对象

// resolve

Promise.resolve('foo')     // 直接返回一个状态为 Fulfilled的Promise对象,'foo'就会作为这个Promise对象所返回的值

    .then(function (value) { // value就是 foo

        console.log(value)

    })

// 等价于

new Promise(function (resolve, reject) {

    resolve('foo')

})

// 使用resolve方法传入的是一个Promise对象,会返回一个原样的Promise

var promise = ajax('/user.json')

var promise2 = Promise.resolve(promise) 

console.log(promise === promise2)  ==> true

Promise.resolve({

    // 传入一个 和 Promise一样的then方法里,thenable(可以被then的对象)

    then: function(onFulfilled, onRejected) {

        onFulfilled('foo')

    }

}).then(function (value) {

    console.log(value) ==> foo

})

Promise的并行执行

并行请求的困难点在于,没办法判断是否全部执行完毕,需要用个东西来判断是否全部执行完毕,就很骚操作

  • Promise.all([]) // 里面参的是一个数组,数组里面的是一个个Promise,等待所有Promise执行结束才会返回新的Promise对象,且是所有Promise成功完成才会成功返回

// 使用Promise.all同步执行多个Promise

var promise = Promise.all([

    ajax('/api.json'),

    ajax('user.json')

])

promise.then(function (value) {

    console.log(value)

}).catch(function (error) {    // 只要有一个Promise失败或者出现异常就会执行

    console.log(error)

})
  • Promise.race() 里面参的是一个数组,数组里面的是一个个Promise,它不会像all一样等所有Promise执行结束才会往下执行,而是只要有一个Promise完成就会返回新的Promise对象

const request = ajax('/api/posts.json')

const timeout = new Promise((resolve, reject) {

    setTimeout(() => reject(new Error('timeout')))

})

// 使用浏览器的 限速功能进行处理

Promise.race([

    request,

    timeout

]).then(value => {

    console.log(value)

}).catch(error => {

    console.log(error)

])

Promise执行时序

JS分为同步任务和异步任务,异步任务又可以分为宏任务,微任务

  • task同步任务:所有同步任务都会在主线程中执行,形成一个执行栈,如图callstack,执行栈会把所有的同步任务执行完毕才会往下调用微任务
  • macrotask宏任务:其实可以理解为每个执行栈执行的代码就是一个宏任务,

    1. 每一个task会从头到尾将这个任务执行完毕,不会执行其他的

    2. 浏览器为了能够使得JS内部task与DOM任务能够有序的执行,会在一个task执行结束后,在下一个task执行开始之前,对页面进行渲染

    3. 常见的宏任务:setTimeout,setInterval,IO

    4. 宏任务执行顺序也是通过队列进行存放的,先进先出机制

  • microtask微任务:可以理解为在当前task执行结束后立即执行的任务是同步任务里所有代码执行完毕之后,紧接着开始执行的任务,

    1. 也就是说,在当前task任务后,在下一个task之前,渲染之前

    2. 所以它的响应速度相比setTimeout(setTimeout是task)会更快,因为不需要渲染

    3. 即在某一个macrotask执行完后,就会将在它执行期间产生的所有microtask执行完毕(在渲染之前)

    4. 常见的微任务:Promise && MutationObserver && process.nextTick

image.png

所以,总结下运行机制:

  1. 执行一个宏任务(栈中没有就从事件队列中获取)
  2. 执行过程中如果遇到微任务,就将它添加到微任务的任务队列中
  3. 宏任务执行完毕后,立即执行当前微任务队列中的所有微任务(依次执行)
  4. 当前宏任务执行完毕,开始检查渲染,然后GUI线程接管渲染
  5. 渲染完毕后,JS线程继续接管,开始下一个宏任务(从事件队列中获取)

Generator异步执行方案


tv_哇
10 声望0 粉丝

軟件迷途中。。。