一 同步和异步、回调函数
js的执行是单线程的,如此设计的原因是如果多个线程去操作dom就会变得复杂难控;因为是单线程,所以对于耗时较长的任务如果也同步执行的话就导致阻塞,所以有了异步;<br /> js引擎执行代码的时候,对于同步任务,首先会在执行栈里面创建一个匿名函数,或者叫执行环境;然后代码先压入栈,然后执行,执行完了出栈;对于声明的函数,不会立即入栈,只有在调用的时候才会入栈,调用完毕出栈;等所有代码执行完毕,清空栈;<br /> 在有异步任务的时候,会涉及到消息队列和内部api;js引擎先执行同步的代码,执行过程中遇到异步代码,会进入到内部api中,如setTimeout,会有浏览器线程单独倒计时这个timer(浏览器线程不是js线程),timer倒计时结束,会把这个timer的回调函数放入消息队列queue中,等同步代码执行完毕,消息队列中的函数会在执行栈中执行回调函数,入栈、出栈等;异步的代码又分宏任务和微任务,事件循环会在执行栈执行完同步任务后开始工作,遍历微任务队列,有的话就放到执行栈中去执行,直到清空了微任务队列,才去遍历宏任务队列,在遍历微任务队列的过程中出现了新的微任务,会加到当前微任务队列的末尾,而不是下个任务的微任务队列;然后再宏任务队列中,有就放到执行栈中去执行,执行完毕后清空栈;<br /> 回调函数时js所有异步的根基。回调函数由调用者定义,交给执行者去执行;
二 promise
promise是es2015新增的对象,参数是个函数,函数的参数有固定的resolve和reject,分别用来传递成功后的状态和失败后的状态;promise初始是pending状态,成功以后编程fullfilled,失败以后变成rejected;一旦状态改变,promise的状态就确定了,不可再更改;
promise例子
function ajax(url) {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();});}ajax('apis/user2.json').then(function(res){console.log(res)}, function(err){console.log(err)});
setTimeout 转换为promise
// setTimeout(function(){// var a = 'hello ';// setTimeout(function(){// var b = ' lagou ';// setTimeout(function(){// var c = 'I love U';// console.log(a + b + c);// }, 10)// },10)// },10);let t = function(str) {return new Promise((resolve, reject) => {setTimeout(()=>{resolve(str)},10)});}let result = '';t('hello').then(function(data){result += data;return t(' there ')}).then(function(data){result += data;return t(' I love U')}).then(function(data) {result += data;console.log(result)})
promise 链式调用
promise的then返回一个全新的promise对象,所以可以链式调用;在then里面返回一个自定义新的promise也可以返回一个值;
后面的then方法就是在为上一个then返回的promise注册回调;前面then方法回调函数返回的值会作为后面then方法回调的参数;如果回调中返回的是promise,那后面then方法的回调会等待它的结束;
promise的异常处理
如果在promis执行的过程中出现了异常,就会触发reject回调;catch方法等同于then方法,只是回调的第一个参数为undefined;catch会捕获前面所有then里面的异常,但是reject只能捕获当前then里面的异常;
全局注册未捕获promise异常;浏览器中用unhandlerdreject注册到window对象,node中在process对象上注册unhandleredRejection事件;
推荐在每个promise中捕获异常,而不是到全局处理;
promise的静态方法
Promise.resove();Promise.reject()方法;通过promise.resolve包裹的promise对象回原样返回;
promise的并行方法
Promise.all([…promise]) 返回一个新的promise对象;等待所有promise结束才会执行then;
Promise.race([…promise]) 返回一个新的promise对象;等待数组中第一个promise结束就会执行then;常用来做接口超时处理;
微任务
promise、mutationObserver、nextTick会在当前任务执行完毕了立即执行;
三 generator
声明的generator函数第一次调用不会立即执行,将生成一个generator对象;后续调用对象的next方法时才会执行对于yield对应的表达式;执行一次,generator对象的指针会停留在当前语句;下次调用时会继续往下执行yield;只有遇到return或者函数结束了,才代表执行结束;此时next会返回done为true的状态;next方法的参数表示上一个yield表达式的返回值,所以在第一次使用next时传参是无效的,v8引擎会忽略第一次next方法的参数,从二次开始,next参数才有意义;
for…of循环可以自动遍历generator函数时生成的iterator对象,且此时不需要调用next方法;
generator的throw方法,可以在函数体内抛出错误,然后再generator函数内捕获;throw方法接受一个参数,该参数会被catch语句接受,建议抛出error对象实例;generator函数体内抛出的错误,可以在函数体外捕获;在generator中执行发生错误,且没有内部捕获就不会再执行下去,如果此后还调用next会返回undefined,done为true的对象,该generator执行完毕;
generator的return方法,可以返回给定的值并且终止遍历generator函数;如果return方法不提供参数,则返回值的value是undefined;如果generator函数内部有try…finaly代码,return方法会直接让其进入finaly代码块;
next、return、throw方法本质上都是为了让generator函数继续执行,只是使用不同的语句替换yield表达式;
如果generator函数嵌套generator函数,则必须在内部的generator函数中遍历,才能依次执行,否则调用内部函数只能返回一个function;此时,用yield表达式标记内部函数,就可以依次执行;
generator案例
function ajax(url) {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();});}// ajax('apis/user2.json').then(function(res){// console.log(res)// }, function(err){// console.log(err)// });function* main() {const user = yield ajax('/apis/user.json');console.log(user);}const g = main();const result = g.next();result.value.then(data => {g.next(data)})
四 async函数
async类似与generator函数;async函数对generator函数的改进体现在: 1 内置执行器;2 更好的语义;3 更广的适用性;
五 手写Promise
1 简单同步
const PENDING = 'pending';const REJECTED = 'rejected';const FULFILLED = 'fulfilled';class MyPromise {constructor(exactor) {exactor(this.resolve, this.reject);}status = PENDING;value = undefined;reason = undefined;resolve = value => {if(this.status != PENDING) return;this.status = FULFILLED;this.value = value;}reject = reason => {if(this.status != PENDING) return;this.status = REJECTED;this.reason = reason;}then = (successCallback, failCallback) => {if(this.status == FULFILLED) {successCallback(this.value)} else if(this.status == REJECTED){failCallback(this.reason)}}}module.exports = MyPromise;
2 添加异步方法
...successCallback = undefined;failCallback = undefined;resolve = value => {...this.successCallback && this.successCallback(this.value);}reject = reason => {...this.failCallback && this.failCallback(this.reason);}then = (successCallback, failCallback) => {...} else {this.successCallback = successCallback;this.failCallback = failCallback;}}}...
3 添加多个then的处理
...successCallback = [];failCallback = [];resolve = value => {...while(this.successCallback.length) this.successCallback.shift()(this.value);}reject = reason => {...while(this.failCallback.length) this.failCallback.shift()(this.reason);}then = (successCallback, failCallback) => {...} else {this.successCallback.push(successCallback);this.failCallback.push(failCallback);}}}module.exports = MyPromise;
4 链式调用
...then = (successCallback, failCallback) => {let promise2 = new MyPromise((resolve, reject)=>{if(this.status == FULFILLED) {let x = successCallback(this.value)resolve(x);} else if(this.status == REJECTED){let y = failCallback(this.reason);reject(y);} else {this.successCallback.push(successCallback);this.failCallback.push(failCallback);}});return promise2;}}...
5 then 返回promise的情况
...then = (successCallback, failCallback) => {let promise2 = new Promise((resolve, reject)=>{if(this.status == FULFILLED) {let x = successCallback(this.value)resolvePromise(x,resolve, reject);} else if(this.status == REJECTED){failCallback(this.reason);} else {this.successCallback.push(successCallback);this.failCallback.push(failCallback);}});return promise2;}}function resolvePromise(x, resolve, reject) {if(x instanceof MyPromise) {x.then(resolve, reject)} else {resolve(x)}}...
6 增加了在构造函数中发生异常、或者在then方法执行中发生异常的处理,另外增加了对then方法无参数回调的判断;
...constructor(exactor) {try{exactor(this.resolve, this.reject);}catch(error) {this.reject(error)}}...then = (successCallback, failCallback) => {successCallback = successCallback? successCallback: value => value;failCallback = failCallback? failCallback: reason => {throw reason};let promise2 = new MyPromise((resolve, reject)=>{if(this.status == FULFILLED) {setTimeout(() => {try {let x = successCallback(this.value)resolvePromise(promise2, x,resolve, reject);} catch (e) {reject(e)}},0)} else if(this.status == REJECTED){setTimeout(() => {try {let x = failCallback(this.reason);resolvePromise(promise2, x,resolve, reject);} catch (e) {reject(e)}},0)} else {this.successCallback.push(() => {setTimeout(() => {try {let x = successCallback(this.value)resolvePromise(promise2, x,resolve, reject);} catch (e) {reject(e)}},0)});this.failCallback.push(()=> {setTimeout(() => {try {let x = failCallback(this.reason);resolvePromise(promise2, x,resolve, reject);} catch (e) {reject(e)}},0)});}});return promise2;}}...
7 promise all方法
...static all = (array) => {let result = [];let index = 0;return new MyPromise((resolve, reject) => {function setData(key, value) {result[key] = value;index ++;if(index === array.length) {resolve(result)}}for (let i = 0; i < array.length; i++) {if (array[i] instanceof MyPromise) {// promisearray[i].then(value => {setData(i, value)}, reason => {reject(reasonW)});} else {// 非promisesetData(i, array[i]);}}});};}...
8 promise resolve方法
...static resolve(value) {if(value instanceof MyPromise) return value;return new MyPromise(resolve => {resolve(value)})}...
9 promise finally方法
...finally(finallyCallback){return this.then(value => {finallyCallback();return value;}, reason => {finallyCallback();throw reason;})}...
10 finally方法返回promise
...finally(finallyCallback){return this.then(value => {return MyPromise.resolve(finallyCallback()).then(() => value)}, reason => {return MyPromise.resolve(finallyCallback()).then(()=> { throw reason });})}...
11 catch 方法
...catch(failCallback) {return this.then(undefined, failCallback)}...
