JS Promise

概述

Promise 是异步编程的一种解决方案,比传统的解决方案—回调函数和事件—更合理和更强大。

  1. Promise是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。
  2. 语法上,Promise 是一个对象,从它可以获取异步操作的消息。
  3. Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理

Promise对象的特点

  1. 对象的状态不受外界影响。
    Promise对象代表一个异步操作,有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。
  2. 一旦状态改变,就不会再变,任何时候都可以得到这个结果。
    Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时就称为 resolved(已定型)。

Promise的缺点

  1. 无法取消Promise,一旦新建它就会立即执行,无法中途取消。
  2. 如果不设置回调函数,Promise内部抛出的错误,不会反应到外部。
  3. 当处于pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。
console.log("a");
setTimeout(() => console.log("b"));
let p = new Promise((resolve, reject) => {
  resolve();
  console.log("c");
}).then(() => {
  console.log("d");
});
console.log("e");
// a c e b d

由以上的输出顺序我们可以引出一个概念:宏任务和微任务

宏任务和微任务

每个“线程”都有自己的事件循环。因此,每个Web工作者都有自己的,所以可以独立执行。而同一来源上的所有窗口都共享一个事件循环,因为它们可以同步通信。事件循环持续运行,执行排队的任何任务。事件循环具有多个任务源,可以保证该源中的执行顺序(规范)。
微任务通常对当前执行脚本之后应该发生的事情进行调度,例如响应一批操作,或者在不承担整个新任务的代价的情况下进行异步操作。只要在执行过程中没有其他JavaScript,并且在每个任务结束时,微任务队列就会在回调后进行处理。在微任务期间排队的任何其他微任务都会添加到队列的末尾,并进行处理。
宏任务包括:script(整体代码), setTimeout, setInterval, setImmediate, I/O。
微任务包括: Promises, process.nextTick, Object.observe。
执行顺序:先执行同步代码,遇到异步宏任务则将异步宏任务放入宏任务队列中,遇到异步微任务则将异步微任务放入微任务队列中,当所有同步代码执行完毕后,再将异步微任务从队列中调入主线程执行,微任务执行完毕后再将异步宏任务从队列中调入主线程执行,一直循环直至所有任务执行完毕。


  Promise.resolve().then(()=>{
    console.log('1')
    setTimeout(()=>{
      console.log('2')
    },0)
  })
 
  setTimeout(()=>{
    console.log('3')
    Promise.resolve().then(()=>{
      console.log('4')
    })
  },0)
// 1 3 4 2

1(红色):JS 引擎会把微观任务Promise存入执行栈,把宏观任务setTimeout存入 “任务队列”

2(绿色):主线程率先运行执行栈中的代码,依次输入1,然后把绿框的setTimeout存入 “任务队列”

3(蓝色):执行栈清空以后,会率先读取 “任务队列” 中最早存入的setTimeout(红框的那个),并把这个定时器存入栈中,开始执行。这个定时器中的代码都是微观任务,所以可以一次性执行,依次输出3 和 4

4(紫色):重复第3步的操作,读取 “任务队列” 中最后存入的setTimeout(绿框的那个),输出2

在这里插入图片描述

该代码示例及图片来源:https://blog.csdn.net/jiaojsun/article/details/95035626

Promise的基本用法

ES6 规定,Promise对象是一个构造函数,用来生成Promise实例。

const promise = new Promise(function (resolve, reject) {
	//...do something
	if (/*异步操作成功*/) {
		resolve(value);
	} else {
		reject(error);
	}
});
  1. Promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolve和reject。它们是两个函数,由 JavaScript引擎提供,不用自己部署。
  2. resolve函数的作用是,将Promise对象的状态从“未完成”变为“成功”(即从 pending 变为resolved),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;
  3. reject函数的作用是,将Promise对象的状态从“未完成”变为“失败”(即从 pending 变为rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。

Promise实例生成以后,可以用then方法分别指定resolved状态和rejected状态的回调函数。

promise.then(function(value){
	//value
}, function(error){
	//error
});

then方法可以接受两个回调函数作为参数。

  • 第一个回调函数是Promise对象的状态变为resolved时调用。
  • 第二个回调函数是Promise对象的状态变为rejected时调用。
  • 其中,第二个函数是可选的,不一定要提供。
  • 这两个函数都接受Promise对象传出的值作为参数。

Promise 新建后就会立即执行。

let promise = new Promise(function (resolve, reject) {
  console.log(`Promise`);
  resolve();
});
promise.then(function () {
  console.log("resolved");
});
console.log("Hi");
// Promise Hi resolved
  • Promise新建后立即执行,所以首先输出的是Promise。然后,then方法指定的回调函数,将在当前脚本所有同步任务执行完才会执行,所以resolved最后输出。

调用resolve函数和reject函数时带有参数,那么这些参数会传递给回调函数。

  1. reject函数的参数通常是Error对象的实例,表示抛出的错误;
  2. resolve函数的参数除了正常的值以外,还可能是另一个Promise实例;
var p1=new Promise((resolve, reject)=>{
	//...
});
var p2=new Promise((resolve, reject)=>{
	//...
	resolve(p1);
});
  • 此时,p1的状态就会传递给p2。
  • p1的状态决定了p2的状态,如果p1为Pending,那么p2的回调函数就会等待p1的状态改变;如果p1的状态为resolved或rejected,那么p2的回调将会立即执行。

链式调用

Promise实例可以链式的then( )调用,并且可以一直调用下去(如果需要)。

let promise1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    let t = true;
    if (t) {
      resolve("success");
    } else {
      reject("failed");
    }
  }, 1000);
});
promise1.then((r) => {
    console.log(r);
    return r + "1";
  }).then((r) => {
    console.log(r);
    return r + "2";
  }).then((r) => {
    console.log(r);
  });

通常,遇到异常抛出,会顺着promise链寻找下一个 onRejected 失败回调函数或者由 .catch() 指定的回调函数。

  new Promise((resolve, reject) => {
    console.log("in Promise"); 
    resolve(); 
  }).then(() => {
    throw new Error("then Error");
    console.log("then");
  }).then(() => {
    console.log("then continue");       
  }).catch((err) => {
    console.log("catch error:" + err.message);
  });

建议:利用catch来终止promise链,避免链条中的rejection抛出错误到全局。

当需要将多个Promise任务一起执行时,可以使用Promise.all( )方法。

  • Promise.all( )实参是所有Promise实例的字面量组成的数组,执行完毕的结果是所有输出结果的所组成的数组。
  var p1 = new Promise((res, rej) => {
    setTimeout(() => {
      res("p1");
    }, 1000);
  });
  var p2 = new Promise((res, rej) => {
    setTimeout(() => {
      res("p2");
    }, 2000);
  });
  var p3 = new Promise((res, rej) => {
    setTimeout(() => {
      res("p3");
    }, 3000);
  });
  Promise.all([p1, p2, p3]).then((r) => {
      console.log(r);
    }).catch((err) => console.log(err.messsage));
 // [ 'p1', 'p2', 'p3' ]

async/await

ES2017引入了async函数,使异步操作变得更加方便。

  • async函数返回的Promise对象会运行执行(resolve)异步函数的返回结果,或者如果异步函数抛出异常的话会运行拒绝(reject)。
  async function testAsync() {
    return "hello async";
  }
  console.log(testAsync());
  • async会把返回值传递给Promise.resolve( )。
  testAsync().then((r) => {
    console.log(r);
  });

异步函数可以包含await指令,该指令会暂停异步函数的执行,并等待Promise执行,然后继续执行异步函数,并返回结果。

  • 如果等待的不是 Promise 对象,则返回该值本身。
  • await 关键字只在异步函数内有效。
  function testAsync() {
    //只有返回值、没有返回Promise的函数
    return "hello async";
  }  function testPromise() {
    //返回Promise的函数
    return new Promise((resolve) => {
      resolve("hello Promise");
    });
  }  async function test() {
    var Async_1 = await testAsync();
    var Promise_1 = await testPromise().then();
    console.log(Async_1); //输出 hello async
    console.log(Promise_1); //输出 hello Promise
  }
  test();
  // hello async    hello Promise
async/await总结

总结来源:https://juejin.cn/post/6844903507938541581

Async:定义异步函数

  • 自动把函数转换为 Promise
  • 当调用异步函数时,函数返回值会被 resolve 处理
  • 异步函数内部可以使用 await

Await - 暂停异步函数的执行

  • 当使用在 Promise 前面时,await 等待 Promise 完成,并返回 Promise 的结果
  • await 只能和 Promise 一起使用,不能和 callback 一起使用
  • await 只能用在 async 函数中

Async/Await 底层依然使用了 Promise。
多个异步函数同时执行时,需要借助 Promise.all

async function getABC() {
  let A = await getValueA(); // getValueA 花费 2 秒
  let B = await getValueB(); // getValueA 花费 4 秒
  let C = await getValueC(); // getValueA 花费 3 秒

  return A*B*C;
}

每次遇到 await 关键字时,Promise 都会停下在,一直到运行结束,所以总共花费是 2+4+3 = 9 秒。await 把异步变成了同步

在 Async/Await 语法中,我们可以使用 try/catch 进行错误处理。在 Promise 中的 .catch() 分支会进入 catch 语句。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值