工具: PlayGround


简介

ECMAScript 6 引入了异步操作的特性,主要特性有:

  • Promise 属于对象, 代表一个可能在未来完成的对象操作相关
  • Generator 属于函数, 可以通过迭代器和yield来暂停函数的执行
  • async/await 操作符, 用于修饰Promise或Generator, 具体来说,它不是ES6引入,而是ES8

下面将详细的说明下。


Promise

关于Promise我们先看下它的声明:

interface Promise<T> {
  	/*
  	@func: 用于完成回调相关
  	@param onfulfilled 成功回调,它接受Promise的结果作为参数
  	@param onrejected 失败回调,它接受Promise的错误作为参数
  	@return 返回一个新的Promise对象
  	*/
    then<TResult1 = T, TResult2 = never>(onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | undefined | null): Promise<TResult1 | TResult2>;
  	/*
  	@func: 对与then函数失败状态的补充,用于捕获Promise中的错误
  	@param: 失败回调
  	@return 返回一个新的Promise对象
  	*/
    catch<TResult = never>(onrejected?: ((reason: any) => TResult | PromiseLike<TResult>) | undefined | null): Promise<T | TResult>;
}

简单的实例:

// 创建对象
// 对象接受两个参数: resolve表示成功时的回调函数, reject表示失败时的回调函数
const obj = new Promise(function(resolve, reject) {
    resolve("sucess");
    reject("failed");
})

// 使用then执行成功回调
obj.then((value:any) => {
    console.log(value);			// sucess
})

// 使用catch执行失败回调
obj.catch((value:any) => {
    console.log(value);	
})

注意:

  • 对象创建后就会执行,且无法停止,直到结束。
  • 对象一定要设置resolvereject的回调,否则对象内部会抛出错误,但不会显示到外部。
  • 对象的状态无法把控,同第一点类似

详细说明下thencatch方法的使用:

  • 可以将两个方法放在一起使用, 因为Promise对象的使用只要执行,要么执行成功回调,要么执行失败回调
const obj = new Promise((resolve, reject) => {
   resolve("sucess");
   reject("failed")
});

// 可以理解为要么执行成功回调,要么执行失败回调
obj.then((result) => {
    console.log("Promise resolved:", result);
})
.catch((error) => {
    console.error("Promise rejected:", error);
});
  • 可以将多个then方法链接在一起,他们会按照顺序并独立运行。

    注意: 每次的独立运行都会生成一个新的Promise对象,且接受上一个对象的结果作为参数。

const obj = new Promise((resolve, reject) => {
    resolve(1);
}).then((value) => {
    console.log("执行第1次回调", value);
    return value + 1;
}).then((value) => {
    console.log("执行第2次回调", value);
}).then((value) => {
    console.log("执行第3次回调", value);
    return Promise.resolve("resolve");
}).then((value) => {
    console.log("执行第4次回调", value);
    return Promise.reject("reject");
}).then((value) => {
    console.log("resolve:" + value);
}, (error) => {
    console.log("reject:" + error);
});

/*
// 创建时赋值1,将value+1的结果返回,并生成新的对象
"执行第1次回调",  1 	
// 新对象使用上个结果的参数2,无返回,并生成新的对象
"执行第2次回调",  2
// 新对象调用,上个参数因无返回则默认undefined, 生成新的对象并调用方法,赋值resolve
"执行第3次回调",  undefined 	
// 获取到回调参数结果...
"执行第4次回调",  "resolve"
"reject:reject" 
*/

这个代码可以拆开理解为:

const obj = new Promise((resolve, reject) => {
    resolve(1);
})

const obj_1 = obj.then((value) => {
    console.log("执行第1次回调", value);
    return value + 1;
})

const obj_2 = obj_1.then((value) => {
    console.log("执行第2次回调", value);
})

const obj_3 = obj_2.then((value) => {
    console.log("执行第3次回调", value);
    return Promise.resolve("resolve");
})

const obj_4 = obj_3.then((value) => {
    console.log("执行第4次回调", value);
    return Promise.reject("reject");
})

const obj_5 = obj_4.then((value) => {
    console.log("resolve:" + value);
}, (error) => {
    console.log("reject:" + error);
})

/*
"执行第1次回调",  1 
"执行第2次回调",  2 
"执行第3次回调",  undefined 
"执行第4次回调",  "resolve"
"reject:reject" 
*/
  • 同样的也可以将多个catch方法链接在一起使用,使得错误依次进行。
const promise = new Promise((resolve, reject) => {
  // 模拟异步操作
  setTimeout(() => {
    const randomNumber = Math.random();
    if (randomNumber < 0.5) {
      resolve("Operation completed successfully");
    } else {
      reject("Operation failed");
    }
  }, 2000);
});

promise
  .then((result) => {
    console.log("Promise resolved:", result);
  })
  .catch((error) => {
    console.error("First catch:", error);
    throw new Error("Custom error");
  })
  .catch((error) => {
    console.error("Second catch:", error.message);
  });

// "First catch:",  "Operation failed" 
// "Second catch:",  "Custom error" 

Generator

它是一个函数, 通过yield将执行流程挂起, 通过next()方法执行下一步。

区分Generator函数和普通函数主要有:

  • 函数内存存在yield表达式相关
  • 定义要使用function*

在创建Generator对象后,它并不会立即执行,而是返回一个迭代器对象。迭代器对象可以通过next()方法逐步执行函数内部的代码。简单的实例:

function* loopFunc() {
  for (let i = 0; i < 2; ++i) {
    console.log("value:", i);
    yield i;
  }
}

const func = loopFunc();
const data1 = func.next();  // "value:",  0
console.log(data1);         // {"value": 0,"done": false} 
const data2 = func.next();  // "value:",  1 
console.log(data2);         // {"value": 1,"done": false}

// 注意此处,函数已经执行完毕
const data3 = func.next();
console.log(data3);         // {"value": undefined,"done": true} 

通过next()方法会返回两个对象:

  • value 表示函数yield表达式产生的值
  • done 表示函数是否执行完毕, true表示执行完毕,false表示函数还可以继续执行。

next参数

next()方法一般情况下是不会传入参数的,但支持参数传入,它的定义:

Generator<number, void, unknown>.next(...args: [] | [unknown]): IteratorResult<number, void>

如果传入参数会作为上一个yield语句的返回值。

function* myGenerator() {
  let result = yield;
  yield result + 10;
}

const gen = myGenerator();
// 第一次没有返回值, 故此value默认为undefined
console.log(gen.next()); // { value: undefined, done: false }
// 第二次参数5作为上一个yield的返回值,故此value为15
console.log(gen.next(5)); // { value: 15, done: false }

因为Generator函数返回的是迭代器对象,所以同样可以使用for ...of进行循环遍历

function* myGenerator() {
  yield 1;
  yield 2;
  yield 3;
}

let result: string = ""
for (const value of myGenerator()) {
  result += value;
}
console.log(result);    // "123" 

return()

Generator函数可以使用next()方法获取下一步的执行结果, 也可以使用return()来结束函数的执行。

function* demo() {
  yield 1;
  yield 2;
  yield 3;
}
// 实例1:
const myGen1 = demo();
console.log(myGen1.next());    // {"value": 1,"done": false}
console.log(myGen1.next());    // {"value": 2,"done": false}
console.log(myGen1.next());    // {"value": 3,"done": false}

// 实例2:
const myGen = demo();
console.log(myGen.next());    // {"value": 1,"done": false}
console.log(myGen.return());  // {"value": undefined,"done": true}
console.log(myGen.next());    // {"value": undefined,"done": true}

两个例子进行对比,会发现return()方法强制终止了函数的执行。


yield* 表达式

它可用于在Generator函数中委托另一个Generator函数或可迭代对象的过程。

function* generator1() {
  yield "a";
  yield "b";
}

function* generator2() {
  yield* generator1();
  yield "c";
  yield "d";
}

const gen = generator2();
console.log(gen.next()); // { value: 'a', done: false }
console.log(gen.next()); // { value: 'b', done: false }
console.log(gen.next()); // { value: 'c', done: false }
console.log(gen.next()); // { value: 'd', done: false }
console.log(gen.next()); // { value: undefined, done: true }

至此,大概的用法介绍完毕,Generator函数了解到这几点大概够用了。



async和await

这两个关键字具体来说是从ES2017(ES8)版本引入的,但他们与Promise对象和Generator函数有很大的关联,故此放到了一起来说明。

  • await 只能在异步函数内部使用,可用于等待一个Promise对象
  • async 用于声明函数是异步的, 函数内部可使用await来暂停函数的执行。

简单的实例:

// 实例1
async function demo1() {
  console.log(1);
  console.log(2);
}
demo1();
console.log(3);       // 1,2,3

// 实例2
async function demo2() {
  await 1;
  console.log(1);
  console.log(2);
}
demo2();
console.log(3);       // 3,1,2

关于async, await和Promise对象的实例:

function delay(ms: number): Promise<string> {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve("Delayed message");
    }, ms);
  });
}

async function asyncFunction() {
  console.log("Before await");
  const result = await delay(2000);
  console.log(result);
  console.log("After await");
}

asyncFunction();

// "Before await"  先输出
// "Delayed message" 等待两秒钟后输出
// "After await" 

至此异步操作相关结束。

09-11 18:56