ES6--Promise、Generator及async

ES6诞生以前,异步编程的方法,大概有如下四种:回调函数、事件监听、发布/订阅、Promise对象;ES6中,引入了Generator函数;ES7中,async更是将异步编程带入了一个全新的阶段。

十四、Promise对象

? Promise,就是一个对象,用来传递异步操作的消息,避免了层层嵌套的回调函数。它代表了某个未来才会知道结果的事件(通常是一个异步操作),并且这个事件提供统一的API,可供进一步处理。

(1)对象的状态不受外界影响。有三种状态:Pending(进行中)、Resolved(已完成,又称Fulfilled)和Rejected(已失败)。

(2)一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从Pending变为Resolved和从Pending变为Rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果。

缺点:

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

更多Promise请参考:http://blog.csdn.net/ligang2585116/article/details/51417334

十五、Generator函数

? 从计算机角度看,生成器是一种类协程或半协程,它提供了一种可以通过特定语句或方法使其执行对象暂停的功能。

? Generator函数,返回一个部署了Iterator接口的遍历器对象,用来操作内部指针。以后,每次调用遍历器对象的next方法,就会返回一个有着value和done两个属性的对象。value属性表示当前的内部状态的值,是yield语句后面那个表达式的值;done属性是一个布尔值,表示是否遍历结束。

yield [[expression]]

? yield 关键字使生成器函数暂停执行,并返回跟在它后面的表达式的当前值。可以把它想成是return关键字的一个基于生成器的版本,但其并非退出函数体,而是切出当前函数的运行时,与此同时可以将一个值带到主线程中。yield语句是暂停执行的标记,而next方法可以恢复执行。

function* gen(){
  yield ‘li‘;
  yield ‘gang‘; // 有误!!!
  return ‘!‘;
}
var g = gen();
g.next(); // {value: ‘li‘, done: false}
g.next(); // {value: ‘gang‘, done: false}
g.next(); // {value: ‘!‘, done: true}

(1)遇到yield语句,就暂停执行后面的操作,并将紧跟在yield后面的那个表达式的值,作为返回的对象的value属性值;

(2)下一次调用next方法时,再继续往下执行,直到遇到下一个yield语句;

(3)如果没有再遇到新的yield语句,就一直运行到函数结束,直到return语句为止,并将return语句后面的表达式的值,作为返回的对象的value属性值;

(4)如果该函数没有return语句,则返回的对象的value属性值为undefined。

需要注意的是,yield语句后面的表达式,只有当调用next方法、内部指针指向该语句时才会执行,因此等于为JavaScript提供了手动的“惰性求值”(Lazy Evaluation)的语法功能。

function* gen() {
  yield  123 + 456;
}

? 上述示例中,yield后面的表达式123 + 456,不会立即求值,只会在next方法将指针移到这一句时,才会求值。Generator函数也可以不用yield语句,这时就变成了一个单纯的暂缓执行函数。

function* f() {
  console.log(‘执行了!‘)
}
let gen = f();
setTimeout(function () {
  gen.next()
}, 2000);

next方法的参数

? 注意: yield句本身没有返回值(返回undefined)。next方法可以带一个参数,该参数就会被当作上一个yield语句的返回值。

function* foo(x) {
  var y = 2 * (yield (x + 1));
  var z = yield (y / 3);
  return (x + y + z);
}

var a = foo(5);
a.next();  // Object{value:6, done:false}
a.next();  // Object{value:NaN, done:false}
a.next();  // Object{value:NaN, done:true}

var b = foo(5);
b.next();   // { value:6, done:false }
b.next(12); // { value:8, done:false }
b.next(13); // { value:42, done:true }

? next方法不带参数,导致y的值等于2 * undefined(即NaN),除以3以后还是NaN;next方法提供参数,第一次调用b的next方法时,返回x+1的值6;第二次调用next方法,将上一次yield语句的值设为12,因此y等于24,返回y/3的值8。

? 注意:这个功能有很重要的语法意义。Generator函数从暂停状态到恢复运行,它的上下文状态(context)是不变的。通过next方法的参数,就有办法在Generator函数开始运行之后,继续向函数体内部注入值。也就是说,可以在Generator函数运行的不同阶段,从外部向内部注入不同的值,从而调整函数行为。

function* f() {
  for(let i=0; true; i++) {
    let reset = yield i;
    if(reset) { i = -1; }
  }
}

let g = f();
g.next() // { value: 0, done: false }
g.next() // { value: 1, done: false }
g.next(true) // { value: 0, done: false }

for…of循环

for...of循环可以自动遍历Generator函数时生成的Iterator对象,且此时不再需要调用next方法。

function *foo() {
  yield 1;
  yield 2;
  return 3;
}
for (let v of foo()) {
  console.log(v);
}

利用Generator函数和for...of循环,实现斐波那契数列

function* fibonacci() {
  let [prev, curr] = [0, 1];
  while (true) {
    [prev, curr] = [curr, prev + curr];
    yield curr;
  }
}
for (let n of fibonacci()) {
  if (n > 1000) break;
  console.log(n);
}

yield* [[expression]]

? yield* 一个可迭代对象,就相当于把这个可迭代对象的所有迭代值分次 yield 出去。表达式本身的值就是当前可迭代对象迭代完毕(当done为true时)时的返回值。

function* gen(){
  yield [1, 2];
  yield* [3, 4];
}
var g = gen();
g.next(); // {value: Array[2], done: false}
g.next(); // {value: 3, done: false}
g.next(); // {value: 4, done: false}
g.next(); // {value: undefined, done: true}

判断是否为Generator函数

function isGenerator(fn){
  // 生成器示例必带@@toStringTag属性
  if(Symbol && Symbol.toStringTag) {
    return fn[Symbol.toStringTag] === ‘GeneratorFunction‘;
  }
}

十六、async函数

? async函数可以理解为Generator函数的语法糖,使用async内置了执行器,无需调用next方法进行逐步调用。且其返回值为Promise。

基本用法

? async函数返回一个 Promise 对象,可以使用then方法添加回调函数。当函数执行的时候,一旦遇到await就会先返回,等到异步操作完成,再接着执行函数体内后面的语句。

async function gen(x){
    var y = await x + 2;
    var z = await y + 2;
    return z;
}
gen(1).then(result => console.log(result), error => console.log(error));
gen(1).then(result => console.log(result)).catch(error => console.log(error));

5秒以后,输出hello world

/*回调方式*/
function test(callback) {
  setTimeout(() => callback(‘hello world‘), 5000);
}
var res = test((value) => console.log(value));
console.log(res);

/*Promise方式*/
function test() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      return resolve(‘hello world‘);
    }, 5000)
  });
}
test().then((value) => console.log(value));

/*async方式*/
async function test() {
  return await new Promise((resolve, reject) => {
    setTimeout(() => {
      return resolve(‘hello world‘);
    }, 5000)
  });
}
test().then((value) => console.log(value));

await命令

正常情况下,await命令后是一个Promise对象。如果不是,会被转成一个立即resolve的Promise对象。

/*成功情况*/
async function f() {
  return await 123;
}
f().then(value => console.log(value));  // 123
/*失败情况*/
async function f() {
  return Promise.reject(‘error‘);
}
f().catch(e => console.error(e));   // error

错误处理

async function f() {
  await new Promise((resolve, reject) => {
    throw new Error(‘出错了‘);
  })
}
f().then(v => console.log(v))
  .catch(e => console.error(e));  // Error: 出错了

注意点

await只能用在async函数中,不能用在普通函数中

/* 错误处理 */
function f(db) {
  let docs = [1, 2, 3];
  for(let doc of docs) {
    await db.push(doc);
  }
  return db; // Uncaught SyntaxError: Unexpected identifier
}

/* 正确处理(顺序执行) */
async function f(db) {
  let docs = [1, 2, 3];
  for(let doc of docs) {
    await db.push(doc);
  }
  return db;
}
let ary = [];
f(ary);
console.log(ary); // [1, 2, 3]

/* 正确处理(并发执行) */
async function f(db) {
  let docs = [1, 2, 3];
  let promises = docs.map(doc => db.push(doc));
  db = await Promise.all(promises);
  return db;
}
let ary = [];
f(ary);
console.log(ary); // [1, 2, 3]

await后面可能存在reject,需要进行try…catch代码块中

async function f() {
  try {
    await Promise.reject(‘出错了‘);
  } catch(e) {
    console.error(e);
  }
  return Promise.resolve(‘hello‘);
}
f().then(v => console.log(v));   // 出错了 hello

多个异步操作,如果没有继承关系,最好同时触发

async function f() {
  let [foo, bar] = await Promise.all([getFoo(), getBar()]);
  return [foo, bar];
}

十七、Promise和async使用场景

? 下述描述的使用场景,只是自己在开发中经常遇到的,并不一定完全符合所有人的使用,只是这样用起来会很便利和实现一些比较难处理的情况。

一个过程中同时存在异步、同步情况,请使用Promise

/*常规方式,错误!不能实现*/
function test(bool) {
  // bool为true,直接返回"hello"
  // bool为false,进行异步请求,这里使用setTimeout代替异步过程
  if(bool) {
    return "hello";
  } else {
    setTimeout(() => {
      return "world";
    }, 5000);
  }
}
test(true);  // "hello"
test(false); // 无任何输出内容

/*Promise正确方式*/
function test(bool) {
  // bool为true,直接返回"hello"
  // bool为false,进行异步请求,这里使用setTimeout代替异步过程
  return new Promise((resolve, reject) => {
    if(bool) {
        return resolve("hello");
    } else {
        setTimeout(() => {
          return resolve("world");
        }, 5000);
    }
  });
}
test(true).then(v => console.log(v));   // ‘hello‘
test(false).then(v => console.log(v));  // 大约5s后输出 ‘world‘

包裹本身不支持async的函数,且hold住当前请求

import fs from "fs";

async function readFile(filepath) {
    return await new Promise((resolve, reject) => {
        fs.stat(filepath, (error) => {
            if(error) {
                return reject("文件不存在!");
            }
            let content = fs.readFileSync(filepath, "utf8");
            return resolve(content);
        })
    })
}
// 测试
readFile(__filename).then((content) => {
    console.log(content)
}).catch((error) => {
    console.error(error);
});
时间: 2024-10-12 19:05:07

ES6--Promise、Generator及async的相关文章

es6,promise,generator,next,yield与koa

es6里有了很多新特性 promise是一种形式,使用promise可以让异步的代码像同步代码,从而更符合人类的思维方式,使回调函数的写法变得轻松 在函数中使用promise,一般是return一个用promise包裹的函数,如: 1 function t(arg){ 2 //准备工作,处理等 3 // ... 4 var istrue =arg+1 5 //处理结束 6 return new promise(function(resolve,reject{ 7 //判定并返回 8 if(ist

一个例子读懂 JS 异步编程: Callback / Promise / Generator / Async

JS异步编程实践理解 回顾JS异步编程方法的发展,主要有以下几种方式: Callback Promise Generator Async 需求 显示购物车商品列表的页面,用户可以勾选想要删除商品(单选或多选),点击确认删除按钮后,将已勾选的商品清除购物车,页面显示剩余商品. 为了便于本文内容阐述,假设后端没有提供一个批量删除商品的接口,所以对用户选择的商品列表,需要逐个调用删除接口. 用一个定时器代表一次接口请求.那思路就是遍历存放用户已选择商品的id数组,逐个发起删除请求del,待全部删除完成

Promise原理讲解 async+await应用(异步回调解决方案)

1.异步编程 1.1.回调函数 1.2 发布订阅 1.3 观察者模式 1.4 Promise 2.promise用法与原理 2.1 Promise.prototype.then() 2.2 Promise.prototype.catch() 2.3 Promise.all 2.4 Promise.race 2.5 Promise.resolve 2.6 Promise.reject 2.7 promise的一些扩展库 2.8 应用 async + await = generator + co 3

初识Generator和Async函数

从promise出现开始,JavaScript一直在致力于简化异步编程的流程,帮助开发者摆脱回调地狱的困境. 在ES6规范中引入新的概念Generator,由此node的框架koa迅速采用,并实现了co来帮助进行迭代, 而ES7中出现的Async函数更是将异步简化成了"同步",可以让我们以接近编写同步代码的方式来编写异步代码(无需使用.then()或者回调函数),下面就将依次介绍这两种方法的区别与相似之处. Generator 说到Generator我们首先来了解下Iterator(遍

Promise/Generator/Co

---恢复内容开始--- 这三个都是为解决回调而生的, 最近在学习Koa框架, Koa框架主要就是将异步的写法变成的同步, 解决了回调地狱的问题,也易于流程的控制, 于是找了很多篇文章学习, 终于有点感悟了~ >-< 1.Promise 看名字,Promise(承诺), 很容易联想到, 这个应该是异步操作结果的承诺, 就像数据库操作, 我们要先去find(), find到了之后就去save(), 那我们承诺find到结果后就去save, 这样就实现了异步写法变同步写法 Promise.then

Es6 Promise 用法详解

Promise是什么?? 打印出来看看  console.dir(Promise) 这么一看就明白了,Promise是一个构造函数,自己身上有all.reject.resolve这几个眼熟的方法,原型上有then.catch等同样很眼熟的方法.这么说用Promise new出来的对象肯定就有then.catch方法喽,没错. 那就new一个玩玩吧. var p = new Promise(function(resolve, reject){ //做一些异步操作 setTimeout(functi

ES6 Promise 异步操作

最近越来越喜欢与大家进行资源分享了,并且及时的同步到自己的园子内,为什么呢? 一.小插曲(气氛搞起) 在上个月末,由于领导的高度重视(haha,这个高度是有多高呢,185就好了),走进了公司骨干员工的队列,并参与为骨干员工准备的"高效能人士的七项修炼"课程培训. 那接下来我是不是该简明扼要的说一下七项修炼有哪些,很受用哦. 七项修炼之一:积极主动 ==> 积极心态去处理事情.不怕事. 七项修炼之二:明确方向 ==> 要做到以终为始,将终点作为起点,帮助你实现生活与工作中的重

通过 ES6 Promise 和 jQuery Deferred 的异同学习 Promise

Deferred 和 Promise ES6 和 jQuery 都有 Deffered 和 Promise,但是略有不同.不过它们的作用可以简单的用两句话来描述 Deffered 触发 resolve 或 reject Promise 中申明 resolve 或 reject 后应该做什么(回调) 在 jQuery 中 var deferred = $.Deferred(); var promise = deferred.promise(); 在 ES6 中 var deferred = Pro

JS的ES6的Generator

JS的ES6的Generator 1.Generator函数的概念: ES6提供的解决异步编程的方案之一,现在已经不怎么用了被淘汰了. Generator函数是一个状态机,内部封装了不同状态的数据. 用来生成遍历器对象 暂停函数,yield关键字暂停,next()方法启动,yield可以获得next方法传递过来的数据,如果无数据yield返回undefined,如果有数据则返回数据. 2.Generator使用: 1 function* generatorExample(){ 2 let res

ES6 Promise 对象

es6 Promise 对象是异步编程的一种解决方案.(在javascript世界里,是单线程顺序执行的)从语法上说,Promise 是一个对象,从它可以获取异步操作的消息. Promise 异步操作有三种状态:pending(进行中).fulfilled(已成功)和 rejected(已失败).除了异步操作的结果,任何其他操作都无法改变这个状态.Promise 对象只有:从 pending 变为 fulfilled 和从 pending 变为 rejected 的状态改变.只要处于 fulfi