JavaScript 异步编程 | 青训营笔记

96 阅读6分钟

image.png

这是我参与「第四届青训营 」笔记创作活动的第3天

this的指向问题

  1. 全局作用域或普通函数中this指向全局变量对象window(定时器里面的this指向window)
  2. 方法调用中谁调用this指向谁
  3. 构造函数中this指向构造函数的实例
  4. 一般情况下this的最终指向是那个调用它的对象

js执行机制

单线程——>解决:异步

同步和异步

异步是指可能比较长时间才有结果的才做,如网络请求(ajax) 读取本地文件 定时器

宏任务和微任务

  1. 前者:js主线程中的同步代码、定时器、事件绑定 ajax 读取文件
  2. 后者:promise中的then、async await、generator
  3. 流程:同步程序->process.nextTick->微任务——>宏任务——>setlmmediate->(后面的事件循环)有延时的计时器

同步任务和异步任务

  1. 先执行完执行栈中的所有同步任务,再读取任务队列中的异步任务,将其进入执行栈中,开始执行。
  2. 事件循环:检测是否还有任务

异步又分为:

回调函数

  1. 导致回调地狱
  2. js中用到回调函数的地方很多,除了定时器以外还有事件监听,以及后面会学到的ajax等。

promise

  1. Promise是异步编程得一种解决方案,其实是一个构造函数,自己身上有all、reject、resolve、race这几个方法,原型上有then、catch等方法
  2. Promise对象有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)
  3. 调用resolve才会执行then
特点
  1. 对象的状态不受外界影响
  2. 一旦状态改变,就不会再变,任何时候都可以得到这个结果。

Promise对象的状态改变,只有两种可能:

从pending变为fulfilled和从pending变为rejected。

只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时就称为 resolved(已定型)

 var p=new Promise(function(resolve(异步操作执行成功后的回调函数),reject(异步操作执行失败后的回调函数)){ 
   //executor执行器函数在传入的时候,它会立即执行 
   //做一些异步操作
 })
 console.log(p);  //得到一个promise对象
then方法的使用
//实例化
const p = new Promise(function(resolve, reject) {
	setTimeout(function() {
		//成功
		let data = 'su';
		resolve(data);
		//resolve() 中可以放置一个参数用于向下一个 then 传递一个值
		//失败
		let err = 'shibai';
		reject(err);
	}, 1000);
});
//调用promise对象的then方法
//成功
p.then(function(value) {
	console.log(value);
	//成功的回调回调,回调函数的第一个参数为成功时返回的promise对象
}, function(reason) {
	console.error(reason);
	//失败的回调函数,回调函数的第一个参数为失败的promise对象
})


let p = new Promise((resolve, reject) => {
    resolve('OK');
    //reject('Error')
});
p.then(data=>{
    console.log(data);//ok
},reason=>{
    console.log(reason);//当第返回的结果不是resolve而是上面的reject,则此处打印 error
})
then链式调用

then函数本身为promise构造函数的实例——>then链式调用

  1. 当resolve执行后,promise状态指定为resolved,执行成功的回调
  2. 每一次then的执行中参数的data都为上一次异步函数执行的返回值
  3. 若上一次无返回值,则输出undefined.错误同理
  4. 在结尾加上catch进行错误捕获,用来中断链条,并且捕获错误原因
catch方法的使用
  1. 其实它和then的第二个参数一样,用来指定reject的回调
  2. 在执行resolve的回调(也就是上面then中的第一个参数)时,如果抛出异常了(代码出错了),那么并不会报错卡死js,而是会进到这个catch方法中,而且把错误原因传到了reason参数中。即便是有错误的代码也不会报错了
  3. catch方法接受一个回调函数
let p = new Promise((resolve, reject) => {
    reject('error');
});
p.catch(reason => {
    console.log(reason);
});//error
Promise.resolve,Promise.reject方法的使用

resolve和reject都接收一个参数

如果传入的参数为 非Promise类型的对象, 则返回的结果为成功promise对象

如果传入的参数为 Promise 对象, 则参数的结果决定了 resolve 的结果

reject在任何情况下都返回失败的rejected对象

let p1 = Promise.resolve(521);
//resolve获取到521为非promise对象,返回成功的promise对象,值为传入的值
let p2 = Promise.resolve(new Promise((resolve, reject) => {
    //resolve('Success')//Promise {<fulfilled>: 'Success'}
    reject('Error');
	//当参数为promise对象时,返回的值为内部promise对象执行的结果
}));
console.log(p1);//Promise {<fulfilled>: 521}
console.log(p2);//Promise {<rejected>: 'Error'}
Promise.all方法的使用
  1. 获取一个promise对象组成的数组,它会等待数组中所有的promise对象执行完毕后返回他们各自返回的结果组成的数组
  2. all方法返回的是一个数组,与传入数组的长度相同
  3. then只有当all中数组中所有异步全部得到结果之后,才执行回调,谁跑的慢,以谁为准执行回调
  4. all接收一个数组参数,里面的值最终都算返回Promise对象
  5. Promise.all([p1, p2, p3]).then(function () {
    //都成功则成功
    }, function() {
    //只要有失败,则失败
    })
let p1 = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('OK');
    }, 1000);
})
let p2 = Promise.resolve('Success');
let p3 = Promise.resolve('Oh Yeah');
//调用
const result = Promise.all/resolve([p1, p2, p3]);

setTimeout(function(){
    console.log(result)
    /*
    Promise {<fulfilled>: Array(3)}
    [[Prototype]]: Promise
    [[PromiseState]]: "fulfilled"
    [[PromiseResult]]: Array(3)
    0: "OK"
    1: "Success"
    2: "Oh Yeah"
    length: 3
    [[Prototype]]: Array(0)
    */
},2000)
Promise.race方法的使用

竞赛,谁跑的快,以谁为准执行回调

let p1 = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('OK');
    }, 1000);
})
let p2 = Promise.resolve('Success');
let p3 = Promise.resolve('Oh Yeah');
//调用
const result = Promise.race([p1, p2, p3]);
setTime(function({
    console.log(result) //Promise {<fulfilled>: 'Success'}
})

async await

  1. async后面跟一个函数,表明这个函数是异步执行的,可单独使用
  2. await后面一定要接promise风格的函数且有resolve返回值,否则返回undefined
  3. await必须与async配套使用,表示等待await后面的函数执行完毕后再执行后续代码
  4. async也是基于promise封装的函数,返回值是一个promise对象,也可以调用then方法
  5. 在await之前的代码属于同步调用,在await之后的代码则会进入异步队列,会在前面的await得到返回值以后再执行后续的代码
let step = (time) => {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve("test");
    }, time);
  });
};
async function demo() {
  let word = await step(500);
  console.log(word);
}
demo();//test

//上诉代码首先创建一个返回promise对象的函数,
//再使用async和await关键词,
//word的值会在await得到修饰函数的返回值后赋值。

async function demo() {
  let word1 = await step(500);
  console.log(word1); //test
  let word2 = await step(1000);
  console.log(word2);//test
  return "函数执行完成";
}
demo().then((res) => {
  console.log(res);//函数执行完成
});