系统化学习前端之JavaScript(ES6:异步编程)
前言
JavaScript 异步编程这块东西比较多,涉及到宏任务和微任务,所以单开一个篇幅梳理一下。
同步和异步
同步和异步是一种宏观概念,具体表现在 JavaScript 中,是同步任务和异步任务,即同步函数和异步函数。
同步
同步指函数在 JavaScript 同步执行。
同步函数执行过程:A 函数进入函数调用栈,等待 A 函数执行完成后,B 函数进入函数调用栈,等待 B 函数执行完成后,C 函数...
异步
异步指函数在 JavaScript 异步执行。
异步函数执行过程:A 函数进入函数调用栈,判断 A 函数是否为同步函数,是则等待 A 函数执行完成后,B 函数进入函数调用栈,判断 B 函数是否为同步函数,否则添加至任务队列。
注意:任务队列根据 "执行者" 不同可以分为微任务队列和宏任务队列,具体可参考 V8 中异步回调。
JavaScript 执行流程
JavaScript 执行函数是通过调用栈进行的,按照全局作用域中的函数顺序(先进后出)依次进入函数调用栈执行。
JavaScript 函数执行顺序
-
函数调用栈依次执行全局作用域中的函数任务。
-
同步函数任务,函数调用栈会依次执行完成。
-
同步函数任务执行完成,函数调用栈会执行微任务队列中的微任务(微任务也是函数任务,只不过是回调函数)。
-
执行完微任务队列以后,函数调用栈会执行宏任务队列中的宏任务(宏任务也是回调函数)。
异步编程
JavaScript 是单线程语言,多任务执行必须借助异步编程来实现,而 JavaScript 对异步编程的实现就是回调函数。将一个任务拆分两个部分,执行函数执行一部分,回调函数执行另一部分。
回调函数:函数作为参数传入另一个函数,则该函数称为回调函数,被传参函数为执行函数。
回调地狱
了解了 JavaScript 函数的执行顺序:同步函数 --> 微任务 --> 宏任务。微任务和宏任务其实都是回调函数,那又如何保证回调函数能按照指定顺序执行呢?或者说,一个异步任务拆分成 A,B,C三个部分,该如何保证 A,B,C 顺序执行呢?
常规方法是在回调中调用下一个回调函数,保证顺序执行。如:
setTimeout(function() {
A()
setTimeout(function() {
B()
setTimeout(function () {
C()
}, 300)
}, 200)
}, 100)
上述代码可以保证 A, B, C 三个任务顺序执行,但是,如果存在多个任务呢?那就形成了多层回调函数嵌套而成的回调地狱。
回调地狱会带来阅读性差,难以维护等问题,因此,Promise 应运而生。
Promise
Promise 是允许将回调函数的嵌套,改成链式调用的方案。Promise 对象内部封装了处理异步操作的 API,可以将异步操作以同步操作的流程表达出来。
-
Promise 对象
实例化 Promise 对象
let promise = new Promise(function (resolve, reject) { // 异步任务的第一个部分操作 setTimeout(function () { let res = A() if(res == success) { resolve(successValue) } else { reject(errorValue) } }) })Promise是一个构造函数,接收一个回调函数,回调函数的参数是resolve和reject方法,这两个参数是内置方法,由 JavaScript 引擎提供。实例化过程中,参数回调函数会立即执行,同时执行异步任务第一部分
A()操作,根据A()返回结果:成功执行resolve方法,失败则执行reject方法。实例
promise对象内部保存三个状态,分别是:pending,fulfilled和rejected。根据实例化过程中,A()返回结果不同则状态不同:若A()返回失败或抛出错误,则promise状态会由pending变为rejected,反之,则由pending变为fulfilled。实例
promise对象内部也保存一个结果值,当fulfilled状态时,会存在resolve方法的参数值successValue,当rejected状态时,会保存reject方法的参数值。-
pending-->fulfilledfunction sum(x, y) { return x + y } let promise = new Promise(function(resolve, reject) { console.log('立即执行了回调函数') setTimeout(function () { let res = sum(1,1) if(res === 2) { resolve('success') } else { reject('fail') } }, 1000) }) console.log(promise)
-
pending-->rejectedfunction sum(x, y) { return x + y } let promise = new Promise(function(resolve, reject) { console.log('立即执行了回调函数') setTimeout(function () { let res = sum(1,2) if(res === 2) { resolve('success') } else { reject('fail') } }, 1000) }) console.log(promise)
-
-
Promise 原型方法
原型方法是实例可以直接调用的。
-
thenthen方法接收两个回调函数作为参数,第一个回调函数为成功回调函数;第二个回调函数为失败回调函数,可以缺省。promise实例的状态为fulfilled,则执行第一个回调函数,回调函数的参数为resolve方法的参数,也是promise的结果值。function sum(x, y) { return x + y } let promise = new Promise(function(resolve, reject) { console.log('立即执行了回调函数') setTimeout(function () { let res = sum(1,1) if(res === 2) { resolve('success') } else { reject('fail') } }, 1000) }) console.log(promise) let promise1 = promise.then(function(res) { console.log(res) return 3 }) console.log(promise1)
promise实例的状态为rejected,则执行第二个回调函数,回调函数的参数为reject方法的参数,同样是promise的结果值。function sum(x, y) { return x + y } let promise = new Promise(function(resolve, reject) { console.log('立即执行了回调函数') setTimeout(function () { let res = sum(1,2) if(res === 2) { resolve('success') } else { reject('fail') } }, 1000) }) console.log(promise) let promise1 = promise.then(function(res) { console.log(res) return 3 }, function(err) { console.log(err) return 0 // throw new Error('err') }) console.log(promise1)
注意:
a.
promise.then()会返回一个promise;b. 返回的
promise的状态和结果值可以根据then的回调函数返回值决定;c. 有返回值,则返回值作为
promise的结果值;d. 无返回值,则
promise的结果值为 undefined;e. 状态有无返回值均为
fulfilled,只有当回调函数中抛出错误,状态才会变为rejected。 -
catch当
then方法缺省第二个参数时,可以使用catch方法,该方法接收一个回调函数,同then方法第二个参数。当promise的状态为rejected或者异步操作中抛出错误均会被catch方法捕获,回调函数可以获取错误信息。同样,catch方法也返回一个promise。function sum(x, y) { return x + y } let promise = new Promise(function(resolve, reject) { console.log('立即执行了回调函数') setTimeout(function () { let res = sum(1,2) if(res === 2) { resolve('success') } else { reject('fail') } }, 1000) }) console.log(promise) let promise1 = promise.then(function(res) { console.log(res) return 3 }).catch(function(err) { console.log(err) return 0 }) console.log(promise1)
-
finally无论
promise的状态是fulfilled还是rejected都会执行finally方法的回调函数,类似try...catch...finally。function sum(x, y) { return x + y } let promise = new Promise(function(resolve, reject) { console.log('立即执行了回调函数') setTimeout(function () { let res = sum(1,2) if(res === 2) { resolve('success') } else { reject('fail') } }, 1000) }) console.log(promise) let promise1 = promise.then(function(res) { console.log(res) return 3 }).catch(function(err) { console.log(err) return 0 }).finally(function() { console.log('well done') }) console.log(promise1)
注意:
then和catch会返回一个promise,因此promise可以链式调用,如promise.then().then().then().catch().finally()。同样,catch置于链式调用的最后,可以捕获之前promise或者then返回的promise的rejected状态或抛出的异常,这种情况称之为异常穿透。 -
-
Promise 静态方法
-
allPromise.all接收多个promise对象构成的数组,表示同时执行多个promise对象中异步操作,返回一个promise对象,返回promise对象的状态和结果值由promise数组决定。当多个
promise都执行完成后,返回promise均为fulfilled时,则all返回的promise的状态为fulfilled,结果值为参数数组中promise结果值构成的数组。function sum(x, y) { return x + y } let p2 = new Promise(function (resolve, reject) { setTimeout(function () { let res = sum(1,1) if(res > 1) { resolve(res) } else { reject('p2 fail') } }) }) let p3 = new Promise(function (resolve, reject) { setTimeout(function () { let res = sum(1,2) if(res > 1) { resolve(res) } else { reject('p3 fail') } }) }) let p4 = new Promise(function (resolve, reject) { setTimeout(function () { let res = sum(1,3) if(res > 1) { resolve(res) } else { reject('p4 fail') } }) }) let promise = Promise.all([p2,p3,p4]) console.log(promise)
当多个
promise中有一个执行完成,并返回rejected状态时,则all返回当前promise对象,即第一个执行完成且返回rejected的promise对象。function sum(x, y) { return x + y } let p0 = new Promise(function (resolve, reject) { setTimeout(function () { let res = sum(0,0) if(res > 1) { resolve(res) } else { reject('p0 fail') } }, 200) }) let p1 = new Promise(function (resolve, reject) { setTimeout(function () { let res = sum(1,0) if(res > 1) { resolve(res) } else { reject('p1 fail') } }, 100) }) let p2 = new Promise(function (resolve, reject) { setTimeout(function () { let res = sum(1,1) if(res > 1) { resolve(res) } else { reject('p2 fail') } }) }) let promise = Promise.all([p0,p2,p1]) console.log(promise)
-
racePromise.race()接收多个promise对象构成的数组,表示同时执行多个promise对象中异步操作,返回一个promise对象,返回promise对象为多个promise中第一个执行完成并返回的promise对象。function sum(x, y) { return x + y } let p0 = new Promise(function (resolve, reject) { setTimeout(function () { let res = sum(0,0) if(res > 1) { resolve(res) } else { reject('p0 fail') } }, 200) }) let p1 = new Promise(function (resolve, reject) { setTimeout(function () { let res = sum(1,0) if(res > 1) { resolve(res) } else { reject('p1 fail') } }, 100) }) let p2 = new Promise(function (resolve, reject) { setTimeout(function () { let res = sum(1,1) if(res > 1) { resolve(res) } else { reject('p2 fail') } }, 50) }) let promise = Promise.race([p0,p2,p1]) console.log(promise)
-
anyPromise.any与Promise.all相对应,接收多个promise对象构成的数组,表示同时执行多个promise对象中异步操作,返回一个promise对象,返回promise对象的状态和结果值由promise数组决定。当多个
promise都执行完成后,返回promise均为rejected时,则any返回的promise的状态为rejected,结果值为参数数组中promise结果值构成的数组。function sum(x, y) { return x + y } let p0 = new Promise(function (resolve, reject) { setTimeout(function () { let res = sum(0,0) if(res > 1) { resolve(res) } else { reject('p0 fail') } }, 200) }) let p1 = new Promise(function (resolve, reject) { setTimeout(function () { let res = sum(1,0) if(res > 1) { resolve(res) } else { reject('p1 fail') } }, 100) }) let promise = Promise.any([p0,p1]) console.log(promise)
当多个
promise中有一个执行完成,并返回fulfilled状态时,则any返回当前promise对象,即第一个执行完成且返回fulfilled的promise对象。function sum(x, y) { return x + y } let p0 = new Promise(function (resolve, reject) { setTimeout(function () { let res = sum(0,0) if(res > 1) { resolve(res) } else { reject('p0 fail') } }, 200) }) let p1 = new Promise(function (resolve, reject) { setTimeout(function () { let res = sum(1,0) if(res > 1) { resolve(res) } else { reject('p1 fail') } }, 100) }) let p2 = new Promise(function (resolve, reject) { setTimeout(function () { let res = sum(1,1) if(res > 1) { resolve(res) } else { reject('p2 fail') } }, 300) }) let promise = Promise.any([p0,p2,p1]) console.log(promise)
-
allSettledPromise.allSettled()接收多个promise对象构成的数组,表示同时执行多个promise对象中异步操作,当所有异步操作均完成以后,返回一个promise对象,返回promise对象状态一直为fulfilled,结果值为多个promise状态结果对象组成的数组。function sum(x, y) { return x + y } let p0 = new Promise(function (resolve, reject) { setTimeout(function () { let res = sum(0,0) if(res > 1) { resolve(res) } else { reject('p0 fail') } }, 200) }) let p1 = new Promise(function (resolve, reject) { setTimeout(function () { let res = sum(1,0) if(res > 1) { resolve(res) } else { reject('p1 fail') } }, 100) }) let p2 = new Promise(function (resolve, reject) { setTimeout(function () { let res = sum(1,1) if(res > 1) { resolve(res) } else { reject('p2 fail') } }, 300) }) let promise = Promise.allSettled([p0,p2,p1]) console.log(promise)
-
resolvePromise.resolve接收一个参数,可以将参数转化为promise对象并返回。当不传参时,返回一个
promise对象,状态为fulfilled,结果值为undefined。当参数为
promise对象,则直接返回参数promise对象。当参数为 JavaScript 数据类型,则返回一个状态为
fulfilled,结果值为参数的promise对象。当参数为
thenable对象,则返回一个promise,并立即执行对象的then方法。let thenable = { then: function(resolve, reject) { resolve(42); } }; let p1 = Promise.resolve(thenable); p1.then(function (value) { console.log(value); // 42 }); -
rejectPromise.reject接收一个参数,会返回一个状态为rejected,结果值为参数的promise对象,不传参,则结果值为undefiend。
-
Generator
Generator 是 JavaScrip 异步编程的又一方案。Generator 函数是一个状态机,封装了多个内部状态,通过返回一个生成器对象来保存状态,利用 API 手动获取状态,异步读取结果。
-
生成器函数
Generator 函数又被称为生成器函数,是一种特殊的函数。相比于普通函数,主要标志有两点:
*和yield。function* gen() { yield 'generator start' yield 'generator' return 'generator end' } let g = gen() console.log(g.next()) // {value: 'generator start', done: false} console.log(g.next()) // {value: 'generator', done: false} console.log(g.next()) // {value: 'generator end', done: true} console.log(g.next()) // {value: undefined, done: true}Generator 函数通过
yield和return关键字定义状态,调用 Generator 函数会返回一个可迭代的生成器对象。通过原型方法或者遍历可以读取生成器对象保存的状态。注意:
return也可以定义状态,但一定是定义最后一个状态。yield只能用于生成器函数,用于普通函数会抛出错误。 -
原型方法
-
nextg.next()方法每调用一次可以读取一次生成器对象g中的状态,当状态读取完毕,依旧调用会返回{value: undefined, done: true}。g.next()接收参数实际是向生成器函数传值,可以通过上一次的yield的返回值获取数据。function* fun() { yield 'generator start' let res = yield 'generator' yield res return 'generator end' } let f = fun() console.log(f.next()) // {value: 'generator start', done: false} console.log(f.next()) // {value: 'generator', done: false} console.log(f.next('step three')) // {value: 'step three', done: false} console.log(f.next()) // {value: 'generator end', done: true} -
returng.return方法调用是结束生成器对象g的状态读取,不传参则读取结果为{value: undefined, done: true},传参则 value 为参数值function* fun() { yield 'generator start' yield 'generator' return 'generator end' } let f = fun() console.log(f.next()) // {value: 'generator start', done: false} console.log(f.return('intercept')) // {value: 'intercept', done: true} console.log(f.next()) // {value: undefined, done: true}
-
-
异步操作同步化
异步任务分为 A,B,C三个部分,顺序执行
A(),B(),C()。function* gen() { yield A() yield B() yield C() } let g = gen() g.next() g.next() g.next() -
co 模块
Generator 函数调用需要手动调用,如
g.next()。co 模块可以自动化执行 Generator 函数。const co = require('co') function* gen() { yield 'generator' } co(gen)
async 和 await
async 和 await 是 Generator 函数的语法糖。但实际上功能和语义远强于 Generator 函数。
-
异步函数
异步函数是指使用
async标记的函数,表示存在异步操作,await后接异步操作的promise对象,非promise对象,会通过Promise.resolve处理得到promise对象。async function fun() { let res = await Promise.resolve(1) console.log(res) // 1 return 2 } let f = fun() console.log(f) // Promise<fulfilled, 2>await相当于promise.then可以获取fulfilled状态下promise的结果值。注意:异步函数会返回一个
promise对象,结果值为异步函数返回值,状态在异步函数抛出错误时为rejected,反之为fulfilled。 -
错误处理
await只能获取fulfilled状态下的结果值,无法拦截rejected的异常。需要借助try...catch进行错误处理。async function fun() { try { let res = await Promis.reject('fail') } catch(err) { console.log(err) // fail } } fun()
后记
ES6 拓展的异步编程处理方案,主要包括 Promise,Generator以及async,await,这些异步操作都是程序为执行者处理的,是微任务。而 JavaScript中通过浏览器线程处理的,如定时器,事件监听,网络请求等,是宏任务。
本文来自博客园,作者:深巷酒,转载请注明原文链接:https://www.cnblogs.com/huangminghua/p/17286017.html

系统化梳理 JavaScript 异步编程相关知识点,包括 Promise,generator 生成器函数,以及 async await 异步函数等。
浙公网安备 33010602011771号