JS语法下

异步编程Promise/Generater/Async/Await

Js异步执行的原理

首先,明确一个概念,js是单线程的。 单线程如何异步?
js是一门事件驱动(event-driven)驱动执行的语言,这些语言的top-level不像Oc/java那样是 main,而是一组 event-handler。
js的调用栈是同步的(单线程,一次只能做一件事), 异步调用是把调用函数加入一个队列,等待主栈空闲。

回调函数和事件

下面这个例子是一个原始的异步调用写法,代码中执行了一个定时器,2s后调用传入的func函数,将asyncfun函数传入并执行

  function asyncfun() {
    console.log('asyncfun');
  }
  setTimeout(func, 2000);
  console.log('task');

Promise

Promise 可以看作一个执行容器,有三种状态pending(进行中)、fulfilled(已成功)和rejected(已失败)
只能从pending状态切换到fulfilled或rejected

  • Promise基本用法 then/catch
function asyncfunc() {
  return 1
}

let mypromise = new Promise(function(onFulfilled, onRejected){
  console.log("Promise started");
  let result = asyncfunc()
  if (result === 1) {
    onFulfilled(result)
  } else {
    onRejected('result !== 1')
  }
})

mypromise.then(function(result){
  console.log(result);
}, function(error) {
  console.error(error);
})

console.log('hello mypromise');
// Promise started
// hello mypromise
// 1

Promise一旦创建就会被立即执行,上述例子中,最先打印Promise started,接着打印hello mypromise,最后执行then方法中的函数打印resolve或reject传出来的值1

  • then()

Promise的实例具有then方法,也就是说,then方法是定义在原型对象Promise.prototype上的。
它的作用是为 Promise 实例添加状态改变时的回调函数。
then方法的第一个参数是onFulfilled状态的回调函数,第二个参数(可选)是rejected状态的回调函数。

let myPromise = new Promise(function(onFulfilled, onRejected){})
let thenPromise = myPromise.then(function(onFulfilled){}, function(onRejected){})
console.log(myPromise); // Promise {  }
console.log(thenPromise); // Promise {  }

then() 函数返回的是一个Promise实例,通过打印myPromise和thenPromise可以印证
由于Promise的then()方法返回的是一个Promise实例,我们可以使用then()方法做链式调用

Promise容器中使用fetch进行异步网络请求,链式then()调用进行报文处理

// npm install node-fetch --save
import fetch from 'node-fetch'

function callback(data) {
  console.log("data=", data);
}

function getGithubUser(callback) {
  fetch('https://api.github.com/users/github') // fetch函数返回的实际是一个Promise对象
    .then(function(response) {
      // 报文处理
      if (response.status === 200 ) {
        return response.json()
      } else {
        return `code: ${response.status}`
      }
    }).then(function(json){
      // 返回调用
      callback(json)
    })
  console.log("fetch started");
}

getGithubUser(callback)
  • catch()

Promise实例的catch方法用于指定发生错误时的回调函数,使用效果上相当于是.then(null, onRejected)

let mypromise = new Promise((onFulfilled, onRejected) => {
  onRejected('result !== 1')
})

mypromise.then(null, (error) => {
  console.error(error); //result !== 1
})

mypromise.catch((error) => {
  console.error(error); // result !== 1
})
  • Promise.All()
    Promise.all方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。
  let p1 = new Promise((resolved, rejected)=>{
    resolved(1)
  })
  let p2 = new Promise((resolved, rejected)=>{
    resolved(2)
  })
  let p3 = new Promise((resolved, rejected)=>{
    resolved(3)
  })

  const p = Promise.all([p1, p2, p3]);
  p.then(function (posts) {
    console.log(posts); // [1,2,3]
  }).catch(function(reason){
    console.log(reason);
  });

(1)只有p1、p2、p3的状态都变成resolved,p的状态才会变成resolved,此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数。

(2)只要p1、p2、p3之中有一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。

  • Promise.race()

    Promise.race方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例。 与all()不同的是,只要多个实例中一个实例率先改变状态,p的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给Promise.race()的回调函数。

    网络请求设置超时时间实际场景应用举例:

  import fetch from 'node-fetch'
  const p1 = fetch('https://api.github.com/users/github')
  const p2 = new Promise(function (resolve, reject) {
    setTimeout(() => reject('request timeout'), 50)
  })

  const p = Promise.race([p1, p2])
  p.then((response)=>{
    return response.json()
  }).then((json)=>{
    console.log(json);
  }).catch((msg)=>{
    console.log(msg);
  })

以上代码中,设置超时时间50ms, Promise实例p中p2先一步p1执行完成,调用reject回调,所以将Promise实例p执行catch方法,打印"request timeout"; 如果将超时时间设置为5000ms(根据你的网络状态定义), 保证在5s内完成网络请求,将执行p1的resolve回调,打印报文

  • Promise.resolve() 和 Promise.reject() Promise.resolve()将现有对象转为 Promise 对象,并自动执行resolve(),将Promise状态切换为fulfilled Promise.reject()将现有对象转为 Promise 对象,并自动执行resolve(),将Promise状态切换为rejected
  const p = Promise.resolve('foo')
  // 等价于
  // const p = new Promise(resolve => resolve('foo'))
  p.then((result)=>{console.log(result);}) // foo

  const p2 = Promise.reject('reject error')
  // 等价于
  // const p2 = new Promise((resolve, reject) => reject('reject error'))
  p2.catch((error)=>{console.log(error)})  //reject error
  • 使用Promise编写异步任务
import fetch from 'node-fetch'

function getFoo () {
  return new Promise(function (resolve, reject){
    resolve('foo');
  });
}

function getGithubUser() {
  return fetch('https://api.github.com/users/github') // fetch函数返回的实际是一个Promise对象
}

getFoo().then((response)=>{
  console.log(response);
  getGithubUser().then((response)=>{
    console.log(response.status);
  }, (error)=>{
    console.log(error);
  })
}, (error)=>{
  console.log(error);
})
// foo
// 200

Generater

Generator 函数是 ES6 提供的一种异步编程的解决方案,通过分步执行函数的方式调度任务。

Generater语法

  • 采用 “function*” 申明Generater函数
  • 函数体内使用yield、return、throw表达式
  • 调用函数名.next(), 返回一个对象,对象内包含函数返回值value和done两个属性
  • 调用函数名.throw(), 在Generator函数内部抛出异常

下面例子演示了一个简单的Generater函数的申明和调用,其中getData定义了4个yield表达式和1个return表达式
前4次被调用者使用next方法调用4次,返回不同的返回值和done:false
第5次以后调用时,返回5和done:true
第6次以后调用时,返回undefined和done:true

  function* getData() {
    console.log('getData start');
    yield 1
    yield 2
    yield 3
    yield 4
    return 5
  }
  let generater = getData()
  console.log(generater.next()); // getData start  // { value: 1, done: false }
  console.log(generater.next()); // { value: 2, done: false }
  console.log(generater.next()); // { value: 3, done: false }
  console.log(generater.next()); // { value: 4, done: false }
  console.log(generater.next()); // { value: 5, done: true }
  console.log(generater.next()); // { value: undefined, done: true }

next方法传参:传参替代上一次yield表达式的值

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

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

  var b = foo(5);
  console.log(b.next()); // { value:6, done:false }
  console.log(b.next(12)); // { value:8, done:false } // y= 2*12 = 24
  console.log(b.next(13)); // { value:42, done:true } // z= 13   x+y+z = 5+24+13=42

throw表达式例子

var g = function* () {
  try {
    yield -1;
    yield 0;
    yield 0;
  } catch (e) {
    console.log('generator catch exception:', e);
  }
};

var i = g();
let resCode = i.next();
if (resCode.value === -1) {
  i.throw('resCode.value = -1') // generator catch exception: resCode.value = -1
}

resCode = i.next();
console.log(resCode); // { value: undefined, done: true }
  • 循环调用

循环调用时,一旦next方法的返回对象的done属性为true


function* getData() {
  yield 1
  yield 2
  yield 3
  yield 4
  return 5
}

for (let v of getData()) {
  console.log(v);  // 1, 2, 3, 4
}
  • 使用Generator函数赋值
function* numbers () {
  yield 1
  yield 2
  return 3
  yield 4
}

// 扩展运算符
let array = [...numbers()]
console.log(array); //[1, 2]

// Array.from 方法
let array1 = Array.from(numbers())
console.log(array1);  //[1, 2]

// 解构赋值
let [x, y] = numbers();
console.log(x, y); // 1 2

// for...of 循环
for (let n of numbers()) {
  console.log(n)
}
// 1
// 2
  • Generater函数内部调用其他函数

使用yield*表达式调用其它Generater函数

function getData() {
  return 'data'
}

function* foo() {
  yield 'a';
  yield 'b';
}

function* bar() {
  yield 'x';
  yield* foo(); // 使用yield*表达式调用其它Generater函数
  yield 'y';
  yield getData()  
}

for (let v of bar()){
  console.log(v);
}
// x
// a
// b
// y
// data

Generater 异步流程管理

import fetch from 'node-fetch'

function getFoo () {
  return new Promise(function (resolve, reject){
    resolve('foo');
  });
}

function getGithubUser() {
  return fetch('https://api.github.com/users/github') // fetch函数返回的实际是一个Promise对象
}

const g = function* () {
  try {
    const foo = yield getFoo();
    console.log(foo);
    const githubUser = yield getGithubUser()
    console.log(githubUser.status);
  } catch (e) {
    console.log(e);
  }
};

// 调用执行Generator函数获取结果
const it = g();
function runGenerator(result) {
  if (result.done) {
    return result.value;
  } else {
    return result.value.then(function (result) {
      return runGenerator(it.next(result));
    }, function (error) {
      return runGenerator(it.throw(error));
    });
  }
}
runGenerator(it.next());
// foo
// 200

上面代码的Generator函数g中,有两个异步操作getFoo和getGithubUser,它们分别返回的就是一个Promise对象。函数runGenerator用来处理这个Promise对象,并调用下一个next方法。
Generator的最大优点,就是代码的写法非常像同步操作,如果去除yield命令,简直一模一样。
Redux技术栈中有个优秀的异步任务调度中间件redux-saga就是基于Generator语法特性实现

Async/Await

Async/Await应该是目前最简单的异步方案, async函数实际上是Generator函数的改进
Async/Await基本语法如下:
1.使用async修饰的函数表示这是一个异步函数,await只能用在这个函数里面; 2.使用async修饰的函数返回值是一个Promise对象 2.await 表示在这里等待promise返回结果了,再继续执行;
3.await 后面跟着的应该是一个promise对象(当然,其他返回值也没关系,只是会立即执行,不过那样就没有意义了)

下面的例子中,首先输出’start’,接着sleep 3s,接着输出’end’

var sleep = function (time) {
    return new Promise(function (resolve, reject) {
        setTimeout(function () {
            resolve();
        }, time);
    })
};
var start = async function () {
    // 在这里使用起来就像同步代码那样直观
    console.log('start');
    await sleep(3000);
    console.log('end');
};
console.log(start());
// start
// Promise {  } //async函数返回的是一个promise对象
// end

使用Async/Await来调度异步任务

import fetch from 'node-fetch'

function getFoo () {
  return new Promise(function (resolve, reject){
    resolve('foo');
  });
}

function getGithubUser() {
  return fetch('https://api.github.com/users/github') // fetch函数返回的实际是一个Promise对象
}

const asyncTask = async function() {
  let foo = await getFoo();
  console.log(foo);
  let githubUser = await getGithubUser()
  console.log(githubUser.status);
}

asyncTask()
console.log('asyncTask start');
// asyncTask start
// foo
// 200

  • 类的定义

在ES6之前,js中是通过函数生成实例对象

  function Point(x, y) {
    this.x = x;
    this.y = y;
  }

  Point.prototype.toString = function () {
    return '(' + this.x + ', ' + this.y + ')';
  };

  var p = new Point(1, 2); //函数的构造函数用法
  console.log(p.toString()); //(1, 2)

es6后我们可以使用class定义一个类, 然后通过类的构造函数实例对象

  class PointClass {
    // 构造方法constructor
    constructor(x, y) {
      this.x = x;
      this.y = y;
    }

    toString() {
      return '(' + this.x + ', ' + this.y + ')';
    }
  }

  let p = new PointClass(1, 2)
  console.log(p.toString()); //(1, 2)
  • JS类的实质
  class PointClass {
    // 构造方法constructor
    constructor(x, y) {
      this.x = x;
      this.y = y;
    }

    toString() {
      return '(' + this.x + ', ' + this.y + ')';
    }
  }
  console.log(typeof PointClass); // function
  console.log(PointClass); // [Function: PointClass]
  console.log(PointClass.prototype); // PointClass {}
  console.log(PointClass.prototype.constructor); // [Function: PointClass]
  PointClass.prototype.sayHello = ()=>{
    console.log('hello');
  }
  p.sayHello() //hello

可以看到PointClass实际上是一个function,他的原型对象是PointClass{}

  • 通过prototype和__proto__添加方法

除了在类解构体中定义方法,还可以通过prototype和__proto__添加方法

  class People {
    constructor(name, sex) {
      this.name = name
      this.sex = sex
    }
  }

  let p1 = new People('zhangsan', 'man')
  let p2 = new People('lisi', 'woman')

  console.log(p1.__proto__);
  console.log(p2.__proto__);
  console.log(People.prototype);

  //为函数对象的原型对象定义方法
  People.prototype.sayName = function() {
    console.log(this.name);
  }

  //为普通对象的原型属性定义方法
  p1.sayName()  //zhangsan
  p2.sayName()  //lisi

  p1.__proto__.saySex = function() {
    console.log(this.sex);
  }

  p1.saySex() //man
  p2.saySex() //women
  • 类的静态方法
    在类中定义的方法,都会被实例继承。如果在一个方法前,加上static关键字,就表示该方法不会被实例继承,而是直接通过类来调用,这就称为“静态方法”。
  class People {
    constructor(name, sex) {
      this.name = name
      this.sex = sex
    }
    static classMethod() {
      console.log('hello People class');
    }
  }

  let p = new People()
  p.classMethod() // TypeError: p.classMethod is not a function
  People.classMethod() // hello People class  
  • 类的继承

通过extends关键字继承

class People {
  constructor(name, sex) {
    this.name = name
    this.sex = sex
  }
  getName() {
    return this.name;
  }
  getSex() {
    return this.sex;
  }
}

class Student extends People {
  constructor(name, sex, grade) {
    super(name, sex)
    this.grade = grade
  }
  getGrade() {
    return this.grade
  }
}

let s = new Student('zhangsan', 'man', 'grade 5')
console.log(s.getName(), s.getSex(), s.getGrade()); // zhangsan man grade 5

在上面例子中,Student的构造函数中使用了两个关键字,this和super,只有调用super之后,才可以使用this关键字,否则会报错。

  • 类继承的原理
    在第四章原型部分我们提到过了js语法里的普通对象和函数对象,其中每一个普通对象都有一个内置属性__proto__指向了创建这个对象的构造函数的原型对象,每一个函数对象都有一个prototype指向了函数的原型对象。
    类的继承是通过__proto__和prototype来实现的,需要注意的是class同时拥有__proto__和prototype属性
  class A {
    constructor(name) {
      this.name = name
    }
    getName() {
      return this.name;
    }
  }

  class B extends A {
    constructor(name, grade) {
      super(name)
      this.grade = grade
    }
    getGrade() {
      return this.grade
    }
  }

  console.log(A); // [Function: A]
  console.log(A.__proto__); //[Function]
  console.log(A.prototype); // A {}
  console.log(A.prototype.__proto__); // {}
  console.log(B); //[Function: B]
  console.log(B.__proto__); // [Function: A]
  console.log(B.prototype); // B {}
  console.log(B.prototype.__proto__); // A{}

  let a = new B.__proto__('wangwu')
  console.log(a.getName()); // wangwu

通过以上的例子中可以看出:
a).子类的__proto__属性,指向父类的构造函数
b).子类的prototype属性的__proto__属性,表示方法的继承,总是指向父类的prototype属性

  • 实例的_proto_

    子类实例的__proto__属性的__proto__属性,指向父类实例的__proto__属性

  class A {
    constructor(name) {
      this.name = name
    }
    getName() {
      return this.name;
    }
  }

  class B extends A {
    constructor(name, grade) {
      super(name)
      this.grade = grade
    }
    getGrade() {
      return this.grade
    }
  }
  let a = new A('zhangsan')
  let b = new B('lisi', 'grade 5')
  console.log(a.__proto__);  // A{}
  console.log(b.__proto__);  // B{}
  console.log(b.__proto__.__proto__); // A{}

你可能感兴趣的:(React,Native相关(学习中))