异步操作-Promise,Generator,async的理解与区别

在学ES6的异步编程解决方案时,知道Promise对象,Generator函数与async函数都是ES6为解决异步编程的提供的方案,但在使用时有点懵,so,当然是写写写来帮助巩固知识点和理清思路了~
(写此文章作为我的初学记录,或许以后再回来翻阅时会有不同感想感受与收获hiahiahia~)

一、Promise

promise是一个对象,用于获取异步操作的消息;promise也是一个容器,里面放着某个异步操作的结果。
promise对象思想:每一个异步操作返回一个promise对象,这个promise对象有一个then方法,允许指定回调函数

1.promise对象的两大特点

(1)对象的三个状态不受外部影响
三种状态说明:

  • pending(进行中)
  • fulfilled(已成功)
  • rejected(已失败)

只有异步操作结果可以决定当前是什么状态
(2)状态一旦改变就不会在变
pending->fulfilled
pending->rejected
状态改变之后,添加回调监听,也会立即得到这个结果

promise对象是一个构造函数,用来生成promise实例

const promise = new Promise( 
	function( resolve, reject ) { 
		if(异步操作成功){ 
			resolve(value); 
		}else{ 
			reject(error); 
		} 
	}
 )

构造函数的参数是一个函数,这个函数有两个参数,分别代表这个实例成功或失败之后的回调函数,也就是:

  • resolve函数的作用:将状态pending->resolved,在异步操作成功时调用,并将返回的异步结果(可能是一个promise对象)作为参数传递出去;
  • reject函数的作用:将状态pending->rejected,在异步操作失败时调用,并将异步操作报出的错误作为参数传递出去(抛出错误);
2.Promise对象常用的回调方法
  1. then()
    为promise实例添加状态改变时的回调函数,不管成功或失败(resolved/rejected),只要状态变了就回调
  2. catch()
    用于指定发生错误时的回调函数(捕捉执行过程中抛出的错误进行处理)

一般来说,不要在then()里面定义reject状态的回调函数,也就是说,尽量使用catch方法来捕获错误,如:

promise.then(function(value){}).catch(function(err){})
// 这样可以捕获前面then方法(或许又返回一个promise实例)执行中的错误

而不是:

promise.then(function(value){} , function(err){} )

其实也就是: .then(null, rejectoin) 或 .then(undefined, rejection)

  1. finally()
    用于指定不管promise对象最后的状态如何,都会执行的操作

finally方法的回调函数不接收任何参数,可以把在then中状态为resolved和rejeted时返回的结果,写到finally中,这样就只用写一次了;
如:

 promise.then( result => {return result;}, error => {throw error;} )
  //等同于
 promise.finally(() => {});
  1. all()
    将多个promise实例,包装成一个新的promise实例,如:
const p = Promise.all([p1,p2,p3])	
//p1,p2,p3都是promise实例
//p1,p2,p3的状态都为fulfiled时,p的状态才为fulfiled,且p1,p2,p3的返回值会组成一个数组,传给p的回调函数resolve
//p1,p2,p3只要有一个被rejected,p的状态就是rejected,且第一个被rejected的实例的返回值会传给p的回调函数reject
  1. race()
    同样将多个promise实例,包装成一个新的promise实例
const p = Promise.race([p1,p2,p3])
//p1,p2,p3都是promise实例,只要有一个实例改变了状态,p的状态也会跟着改变,且率先改变的实例的返回值,会传递给p的回调函数
  1. resolve()
    可以将现有对象转为一个新的promise对象

Promise.resolve()方法的参数情况:

  • 参数是一个promise实例:不做任何修改,返回这个实例
  • 参数是一个thenable对象(指的是具有then方法的对象):将这个对象转为promise对象,然后立即执行这个对象的then方法
  • 参数不是对象,或是不具有then方法的对象:Promise.resolve()方法返回一个新的promise对象,状态为resolved
  • 不带任何参数:直接返回一个resolved状态的promise对象 ;const p = Promise.resolved;
  1. reject()
    也可以将现有对象转为一个新的promise实例,这个实例的状态为rejected,如:
cosnt p = Promise.reject('error')
//等同于  
const p = new Promise((resolve, reject) => reject('error'))

~

几个注意点:
then,catch,finally方法定义在原型对象Promise.prototype上,即Promise实例具有这些方法;
then方法指定的回调函数,在当前脚本所有同步任务执行完毕之后才会执行。

二、Generator函数

Generator函数是一个状态机,内部封装了多个内部状态,这些内部状态由yield表达式或return语句来定义;
也是一个遍历器生成函数返回一个遍历器对象,也就是指向内部状态的指针对象,可以遍历generator函数内部的每一个状态;

1.使用

调用generator函数,返回一个遍历器对象,代表generator函数的内部指针;下一步,使用next()方法,使内部指针移向下一个内部状态(yield表达式或return语句)

每次调用next()方法,都会返回一个带有value和done属性的对象,value代表当前内部状态的值(也就是yiled表达式后面那个表达式的值,或return返回的值),done是一个布尔值,表示当前遍历是否结束;

换言之,Generator函数是分段执行的,yield表达式是暂停的标志,next()可以恢复执行;

//定义一个generator函数 
function* test(){
	yield "hello!"; 
	yield "你好!"; 
	return "结束了~";
}
let t = test(); 
t.next();	//调用generator函数test(),执行它的next方法,返回"hello!"
//如果第一个就是return语句,后面再t.next()也只是返回undefined(value值)和true(done值)了

2.内部状态

yield表达式:可以执行多个yield表达式,返回一系列的值
return语句:不具备位置记忆功能,且一个函数只能有一个return语句,就是只能执行一次return

3.yield表达式

另外,需要注意,yield表达式如果放在另一个表达式中,必须加上圆括号;若是作为参数或放在表达式右边(=号右边),可以不加()
遇到yield表达式时,就暂停执行后面的操作,并将紧跟在yield后面的表达式的值,作为返回的对象的value属性值;

function* test(){
	console.log("我是" + (yield '123')); 
	yield "hello!"; 
	yield "你好!"; 
	return "结束了~";
}	
//第一次调用next()时,只返回了value:'123',done:false,并不会返回"我是";
//第二次调用next()时,会返回"我是 undefined"和value:hello,done:false
4.与iterator接口的关系

一个对象的Symbol.iterator方法,等于这个对象的遍历器生成函数(刚好,generator函数就是一个遍历器生成函数),调用这个函数会返回这个对象的一个遍历器对象(也就是具有了iterator接口,可以使用for…of 遍历)
如普通对象就没有部署iterator,不能被遍历

obj[Symbol.iterator] = function* () {
	var key = Object.keys(obj);//返回obj对象的key名的数组
	for(let i=0;i<key.length;i++){ yield key[i]; }
}
5.next方法的参数

yield表达式本身没有值(总是返回undefined),next()的参数可以当作上一个yield表达式的值;这样就可以在generator函数运行的不同阶段向其内部注入不同的值,从而调整函数行为。

三、async函数

(asynchronous 异步的)

1.特点
  • 使用async关键字声明函数;
  • 默认返回一个已解决的promise对象

async表示函数内有异步操作;await表示紧跟在其后的表达式需要等待结果。

async函数返回一个Promise对象,可以使用then方法添加回调函数;而Generator函数返回一个Iterator对象。

(在写法上,与Generator函数的区别就是:把*换成async,把yield换成await;async 函数的实现原理,就是将 Generator 函数和自动执行器,包装在一个函数里。)

await只能在async函数中使用,名为“等待”,await命令后是一个promise对象,如果不是,会被转成一个resolve的promise对象。
如果await后面的promise状态是reject的话会抛出异常,所以可以将await语句写在try catch里。

当async函数执行的时候,一旦遇到await就会先返回,等到异步操作完成,再接着执行函数体内后面的语句,所以调用async函数虽然有等待, 但是并不会导致阻塞, 因为它内部的所有阻塞都封装在promise对象中异步执行。
举个栗子:

function resolveAfter2Seconds(x) {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve(x);
    }, 2000);
  });
}
async function add1(x) {
  const a = await resolveAfter2Seconds(20);
  const b = await resolveAfter2Seconds(30);
  const {c} = await { c: 10}
  return x + a + b + c;
}
add1(10).then(v => {
  console.log(v);  // prints 70 after 4 seconds.
});

async函数内置执行器,与普通函数一样调用执行;Generator函数需要依靠next()函数执行。

2.语法
  1. async函数返回一个Promise对象(一般地,await后面就是一个Promise对象,返回这个对象的结果;若不是一个对象,则返回相应的值)

  2. 函数内部return的值会作为then方法回调函数的参数

  3. 若内部抛出错误,会使Promise对象变为reject状态,且抛出的错误对象(如:throw new
    Error(‘error’))会被catch方法回调函数接收到

  4. 若await后面的对象变为reject状态,那整个async函数都会中断执行,如:

async function f() {
  await Promise.reject('出错了');
  await Promise.resolve('hello world'); // 不会执行
}
3.错误处理

await后面的异步操作出错,表明async函数返回的Promise对象被reject;若是希望在一个异步操作失败之后,仍要执行后面的异步操作,可:
(1)使用try…catch
如将第一个await放入try…catch中,那么不管这个异步操作是否成功,第二个await都会执行;
可以在try…catch外面使用for循环,若异步操作成功,则break退出循环;若失败,会被catch捕捉,进入下一次循环,如:

const superagent = require('superagent');
const NUM_RETRIES = 3;

async function test() {
  let i;
  for (i = 0; i < NUM_RETRIES; ++i) {
    try {
      await superagent.get('http://google.com/this-throws-an-error');
      break;
    } catch(err) {}
  }
  console.log(i); // 3
}

test();

(2)使用catch方法
在第一个await的Promise对象后面添加catch方法处理可能出现的错误,第二个await同样会执行

In brief,Generator与async相比较于Promise对象(的链式调用),写法上更简洁、清晰、明了,传参也更加方便~

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值