Promise Async+await实现原理及案例分析

文章目录

  • 一、Promise
    • 1.1 promise原理
    • 1.2 promise优缺点
  • 二、Async await
    • 2.1 async await原理
    • 2.2 async较Promise的优点
  • 三、Promise 和 Async await 异步执行原理案例分析
    • 3.1 原理分析
    • 3.2 案例分析

一、Promise

1.1 promise原理

promise 可以看作是一个对象,是异步编程的一种解决方案,可以获取异步操作的消息。

  1. promise实际上是一个构造函数,当被创建时便会立即执行里面的内容
new Promise((resolve, reject) => {
  console.log('Promise')
  resolve(1)
})
//结果 new Promise 
  1. promise有三个状态,分别是pending(进行中)、resolved(已成功)和 rejected(已失败),一旦其处于resolved和 rejected状态,便不可再改变。promise的原型有一个then()方法,其接收两个函数作为参数,第一个参数是 Promise 执行成功resolved()时的回调,第二个参数是 Promise 执行失败时rejected()的回调,两个函数只会有一个被调用。成功的回调必选,失败的回调可选。
new Promise((resolve, reject) => {
  console.log('Promise')
  resolve(1)
}).then(function(value){ 
  console.log(value); //此处then调用了resolve(1),输出结果为:Promise  1
  return Promise.reject('reject'); //返回一个promise
}).then(function(value){ // 只能调用其中一个函数,此处调用了reject,输出reject:reject
  console.log('resolve:' + value);
}, function(err) {
  console.log('reject:' + err);
}); 
  1. 通过多次调用.then(),可以添加多个回调函数,它们会按照插入顺序并且独立运行,即Promise 的链式编程。若在 then 中使用了 return,那么 return 的值会被 Promise.resolve()包装,然后被下一个then调用。
const p = new Promise(function(resolve,reject){
  resolve(1);
}).then(function(value){ // 第一个then输出:1
  console.log(value);
  return value * 2;
}).then(function(value){ // 第二个then输出:2
  console.log(value);
}) 

总体过程:启动异步任务 => 返回promie对象 => 给promise对象绑定回调函数(甚至可以在异步任务结束后指定)

1.2 promise优缺点

优点:promise的链式结构可以解决回调地狱问题

缺点:

  • 无法取消 Promise ,一旦新建它就会立即执行,无法中途取消。
  • 如果不设置回调函数,Promise 内部抛出的错误,不会反应到外部。
  • 当处于 pending 状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)

二、Async await

2.1 async await原理

async可以看成promise的进阶,放在函数前面表明这是一个异步函数,会返回一个Promise对象,await必须放在async函数里面,若是放在外面会得到一个语法错误。async 函数执行时,如果遇到 await 就会先暂停执行 ,等到await后面触发的异步操作完成后,恢复 async 函数的执行并返回解析值。

  1. 普通async await执行过程:
let s = 0
let f = async () => {
  s = s + await 10
  console.log('4', s) // -> 4 10
}
f()
s++
console.log('1', s) // -> 1 1
//输出顺序:1 1 4 10 
  • 定义s全局变量
  • 定义了一个f的async异步函数
  • 调用f:在f函数中,遇到await,s等待返回,此时在f函数内s=10,这时候async函数内部且在await下面的代码暂停执行,先执行s++,在外部,s=0,s++则为1
  • console.log(‘1’, s) ->输出 1 1,再回过头去执行await下面的console.log(‘1’, s)
  • 最终输出结果便为:1 1 4 10
  • 此处涉及到js的单线程运行机制,时间循环机制,同步任务和异步任务,宏任务和微任务,详解见下文。
  1. 前面说到async函数返回的是一个promise对象,async 就是将函数返回值使用 Promise.resolve() 包裹起来,当使用then调用时,就相当于Promise在用then调用resolved。
let s = 0
let f = async () => {
  s = s + await 10
  console.log('4', s) // -> 4 10
}
f().then(console.log('3', s))
s++
console.log('1', s) // -> 1 1
//输出顺序:3 0 1 1 4 10 
  • 定义s全局变量
  • 定义了一个f的async异步函数
  • 调用f:在f函数中,遇到await,s等待返回,此时在f函数内s=10,这时候async函数内部且在await下面的代码暂停执行
  • 同时使用then方法添加回调函数或者执行相关操作(此处是直接console出值),因此先执行then函数,输出3 0
  • 接着往下s++,并执行console.log(‘1’, s),最后回到await下面的代码,执行console.log(‘4’, s)

使用then方法添加回调函数的情况:

async function helloAsync(){
    return "Async";
  }
  
console.log(helloAsync())  // Promise {: "Async"}
 
helloAsync().then(val=>{
   console.log(val);         // Async
}) 
  1. await返回值返回的是Promise 对象的处理结果。如果等待的不是 Promise 对象,则返回该值本身。
function testAwait(){
   console.log("testAwait");
}
async function helloAsync(){
   await testAwait();
   console.log("helloAsync");
}
helloAsync();
// testAwait
// helloAsync 
  1. 如果等待的是Promise对象,则resolve,然后再恢复 async 函数的执行并返回解析值
function testAwait (x) {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve(x);
    }, 2000);
  });
}
 
async function helloAsync() {
  var x = await testAwait ("hello world");
  console.log(x); 
}
helloAsync ();
// hello world 

2.2 async较Promise的优点

  • 同步化代码的阅读体验(Promise 虽然摆脱了回调地狱,但存在 then 链式调⽤的阅读负担)
  • 和同步代码更一致的错误处理方式( async/await 可以⽤成熟的 try/catch 做处理,比 Promise 的错误捕获更简洁直观)
  • 调试时的阅读性, 也相对更友好

三、Promise 和 Async await 异步执行原理案例分析

3.1 原理分析

在js的运行机制中,程序是单线程运行的,就是说一次只能执行一个任务,其他任务只能等待被执行。任务分为同步任务和异步任务,异步任务又分为宏任务和微任务。可以这样来理解,同步任务放在执行栈(调用堆)中,异步任务放在任务队列中。主线程每次运行时,所有同步任务都按照顺序排列在执行栈(调用堆)中依次等待主线程调用和执行,同步任务都执行完毕之后,再去任务队列中调用微任务和宏任务,执行顺序便是,每清空一次所有的微任务,再执行一次宏任务,以此循环,这个过程又叫js的事件循环机制。 宏任务:所有 js 代码,setTimeOut、setInverter、setImmediate 、 MessageChannel,I/O,UI Rendering等。 微任务:promise 的 then,catch,finaly,process.nextTick,MutationObserver(监测 dom 的变更)等。

3.2 案例分析

console.log("script start");
Promise.resolve().then(function(){
    console.log("promise1")
})
setTimeout(function(){
    console.log("setTimeout")
},0);
Promise.resolve().then(function(){
    console.log("promise2")
})
console.log("script end");
​
//输出
//script start
//script end
//promise1
//promise2
//setTimeout 
微任务 宏任务
promise1 setTimeout
promise2
解析:
  • 从上往下执行,script start和script end是同步任务,先输出(满足同步任务先执行的条件)
  • promise1是微任务,先放到微任务队列
  • setTimeout是宏任务,放到宏任务队列
  • promise2是微任务,继续放到微任务队列
  • 先清空微任务,因此按顺序输出promise1和promise2
  • 再来执行宏任务setTimeout
  • 输出 script start script end promise1 promise2 setTimeout
console.log('script start');
Promise.resolve().then(function() {
    setTimeout(function() {
      console.log('setTimeout1');
    }, 0);
}).then(function() {
  console.log('promise1');
});
​
setTimeout(function() {
    console.log('setTimeout2')
    Promise.resolve().then(function(){
        console.log('promise2');
    })
},0)
console.log('script end');
​
//输出:
//script start
//script end
//promise1
//setTimeout2
//promise2
//setTimeout1
​ 
微任务 宏任务
promise1 setTimeout2
promise2 setTimeout1
  • 先清空第一个微任务1 promise1,再执行一个与微任务1 并列的宏任务1 setTimeout2
  • 再继续查找微任务,在宏任务内嵌套了一个微任务2 promise2,先清空,再执行另一个与微任务2 并列的宏任务2 setTimeout1
  • 输出: script start script end promise1 setTimeout2 promise2 setTimeout1
console.log('script start')
async function async1() { 
await async2()
console.log('async1 end')
}
async function async2() {
console.log('async2 end')
}
async1()
setTimeout(function () {
console.log("setTimeout")
})
new Promise(resolve => {
console.log('promise')
resolve()
}).then(function () {
console.log("promise1")
})
.then(function () {
console.log("promise2")
})
console.log('script end') 

分为主线程的执行栈和任务队列 主线程:先执行完同步任务 Promise Async+await实现原理及案例分析_第1张图片 任务队列:先清空微任务,再执行宏任务 Promise Async+await实现原理及案例分析_第2张图片 整个程序的输出结果为: script start async2 end promise script end async1 end promise1 promise2 setTimeout 这里可能有同学会疑惑这里的script start和script end为什么不是跟前面案例一样一起输出。其实都是按顺序执行下来的,这个也是,只是前面的案例中途没有输出其他的,反应比较快。

小tip:微任务队列清空后执行宏任务,执行宏任务队列时不会等队列清空再去执行微任务,执行一个宏任务之后就会去检查清空微任务队列,再回来执行宏任务队列。

你可能感兴趣的:(javascript,前端,typescript)