Promise & Async/Await

226 阅读4分钟

promise和async await 都是 js 异步编程的解决方案

Promise

基本概念和用法

Promise是一个构造函数,通过new来构建一个promise对象,,该对象必须接受一个函数作为参数这个函数有resolve和reject两个参数,这两个函数, 用于改变 promise 的状态和传递异步操作的结果,promise有三种状态(pending、fulfilled、rejected)一旦函数执行完成 promise 有了结果状态无法改变

Promise解决了传统callback函数回调地狱的问题,支持链式调用,但是遇到复杂的业务场景,promise 嵌套 .then 的写法一样不够直观太容易阅读,也不方便处理异常

创造promise实例后,它会立即执行。
从表面上看,Promise只是能够简化层层回调的写法,而实质上,Promise的精髓是“状态”,用维护状态、传递状态的方式来使得回调函数能够及时调用,它比传递callback函数要简单、灵活的多

image

一个 Promise对象有以下几种状态:  (状态一旦改变,就不会再变)
pending: 初始状态,既不是成功,也不是失败状态。
fulfilled: 意味着操作成功完成。
rejected: 意味着操作失败。

let p = new Promise(
    /* 执行器 executor */
    function (resolve, reject) {
        // 一段耗时很长的异步操作
        resolve(data); // 数据处理完成
        reject(error); // 数据处理出错
    }
)
p.then(() => {
       // 成功,下一步
  }, () => {
       // 失败,做相应处理
});
// 或用catch来指定reject的回调
p.then((data) => {
     console.log('resolved',data);
}) 
.catch((err) => {
     console.log('rejected',err);
});
.finally(() => {
     // ES9 新增方法
     // 这里可以写无论异步执行结果的状态是成功还是失败都要执行的操作
}); 

promise的相关API

  1. **Promise的all方法
    **可以并行执行多个异步函数,并且在所有异步操作执行完后执行回调。

    let Promise1 = new Promise(function(resolve, reject){})
    let Promise2 = new Promise(function(resolve, reject){})
    let Promise3 = new Promise(function(resolve, reject){})
    
    let p = Promise.all([Promise1, Promise2, Promise3])
    
    p.then(funciton(){
      // 三个都成功则成功  
    }, function(){
      // 只要有失败,则失败 
    })
    
  2. Promise的race方法
    并行执行多个异步函数,以最先执行完成的异步操作结果状态来执行回调

    模拟超时处理:可以用race给某个异步请求设置超时时间,并且在超时后执行相应的操作。
    //请求某个图片资源
       function requestImg(){
           var p = new Promise((resolve, reject) => {
               var img = new Image();
               img.onload = function(){
                   resolve(img);
               }
               img.src = '图片的路径';
           });
           return p;
       }
    
       //延时函数,用于给请求计时
       function timeout(){
           var p = new Promise((resolve, reject) => {
               setTimeout(() => {
                   reject('图片请求超时');
               }, 5000);
           });
           return p;
       }
    
       Promise.race([requestImg(), timeout()]).then((data) =>{
           console.log(data);
       }).catch((err) => {
           console.log(err);
       });
    
  3. **Promise的allSettled方法
    **并行执行多个异步函数,记录并返回每个异步操作的结果状态

promise在事件循环中的执行顺序

Async/Await

基本概念和用法

ES7 引入了 async/await,提供了在不阻塞主线程的情况下使用同步代码实现异步访问资源的能力,并且使得代码逻辑更加清晰,而且还支持 try-catch 来捕获异常

async 是一个通过异步执行并隐式返回 Promise 作为结果的函数。

可以说async 是Generator函数的语法糖,并对Generator函数进行了改进。

async函数对 Generator 函数的改进,体现在以下四点:

  • 内置执行器。Generator 函数的执行必须依靠执行器,而 async 函数自带执行器,无需手动执行 next() 方法。
  • 更好的语义。async和await,比起星号和yield,语义更清楚了。async表示函数里有异步操作,await表示紧跟在后面的表达式需要等待结果
  • 更广的适用性。co模块约定,yield命令后面只能是 Thunk 函数或 Promise 对象,而async函数的await命令后面,可以是 Promise 对象和原始类型的值(数值、字符串和布尔值,但这时会自动转成立即 resolved 的 Promise 对象)。
  • 返回值是 Promiseasync 函数返回值是 Promise 对象,比 Generator 函数返回的 Iterator 对象方便,可以直接使用 then() 方法进行调用。
function run(gen){  
  var g = gen();

  function next(data){
    var result = g.next(data);
    if (result.done) return result.value;
    result.value.then(function(data){
      next(data);
    });
  }

  next();
}

原理

async function fn(args) {
  // ...
}

等同于:

function fn(args) {
  return spawn(function* () {
    // ...
  });
}
// spawn函数就是自动执行器
function spawn(genF) { 
  return new Promise(function(resolve, reject) {
    const gen = genF();

    function step(nextF) {
      let next;
      try {
        next = nextF();
      } catch(e) {
        return reject(e);
      }
      if(next.done) {
        return resolve(next.value);
      }
      Promise.resolve(next.value).then(function(v) {
        step(function() { return gen.next(v); });
      }, function(e) {
        step(function() { return gen.throw(e); });
      });
    }

    step(function() { return gen.next(undefined); });
  });
}

在事件循环中的执行顺序

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')
}, 0)

new Promise(resolve => {
  console.log('Promise')
  resolve()
})
.then(function() {
  console.log('promise1')
})
.then(function() {
  console.log('promise2')
})
// script start => async2 end => Promise => script end => async1 end => promise1 => promise2 => setTimeout

相关面试题

juejin.cn/post/684490…

参考资料:

ES6 系列之我们来聊聊 Async