Promise、Generator 函数与 async/awai

2017-09-22  本文已影响0人  执凉

前言

本次分享的内容是 Promise、Generator 函数与 async/await ,都是与异步操作相关的内容,我们常见的异步操作基本是:回调函数事件监听,AJAX就是事件监听的一种,本次分享中也主要以AJAX为例。

var request = new XMLHttpRequest();

request.onreadystatechange = function () {
    if (request.readyState === 4) {
        if (request.status === 200) {
            return success(request.responseText);
        } else {
            return fail(request.status);
        }
    }
}

request.open('GET', '/api/categories');
request.send();

Promise

首先先看一下Promise是什么:

Promise.png

可以看到Promise是一个构造函数,里面有我们熟悉的all、 reject 、resolve 方法,原型上有 then、catch 方法,我们可以新建一个Promise 对象来使用。

new Promise(
    /* executor */
    function(resolve, reject) {...}
);

我觉得文档上说的比我清楚的多:

Promise对象是一个代理对象(代理一个值)。它允许你为异步操作的成功和失败分别绑定相应的处理方法(handlers )。 这让异步方法可以像同步方法那样返回值,但并不是立即返回最终执行结果,而是一个能代表未来出现的结果的promise对象
一个 Promise有以下几种状态:

  • pending: 初始状态,不是成功或失败状态。
  • fulfilled: 意味着操作成功完成。
  • rejected: 意味着操作失败。
    pending 状态的 Promise 对象可能触发fulfilled 状态并传递一个值给相应的状态处理方法,也可能触发失败状态(rejected)并传递失败信息。当其中任一种情况出现时,Promise 对象的 then 方法绑定的处理方法(handlers )就会被调用(then方法包含两个参数:onfulfilled 和 onrejected,它们都是 Function 类型。当Promise状态为fulfilled时,调用 then 的 onfulfilled 方法,当Promise状态为rejected时,调用 then 的 onrejected 方法, 所以在异步操作的完成和绑定处理方法之间不存在竞争)。
    因为 Promise.prototype.then 和 Promise.prototype.catch
    方法返回promise 对象, 所以它们可以被链式调用。
promises.png
疑问

既然 .then() 既有reject 也有 resolve的回调,那为什么还有.catch()?

我看网上的说法是说 如果是 .then() 中报错,则会进入 .catch() 中处理,这样可以避免报错,类似于 try catch。

基本api

应用

然后我们用Promise来重写一下ajax:

function ajax (method, url, data) {
    var request = new XMLHttpRequest();
    return new Promise(function (resolve, reject) {
        request.onreadystatechange = function () {
            if (request.readyState === 4) {
                if (request.status === 200) {
                    resolve(request.responseText);
                } else {
                    reject(request.status);
                }
            }
        };
        request.open(method, url);
        request.send(data);
    });
}

var p = ajax('GET', '/api/xxxx');
p.then(function (text) {
    console.log(text);
}).catch(function (status) {
    console.log('ERROR: ' + status);
});

看起来好像并没有什么改变,但是如果我们想一个请求完成再发起另一个请求呢?

ajax ('GET',  '/api/hhh').then(function(data) {
  return ajax('POST', '/api/hhhh', data.body);
})

.then(function(data) {
  return ajax('POST', '/api/hhhhh', data.body);
})

.then(function() {
   console.log(data);
});

肯定比无限回调好看多了。

除此之外,我们可以使用 Promise.all() 来并行执行异步操作。

function func(num){
    var p = new Promise(function(resolve, reject){
        setTimeout(function(){
            resolve(num);
        }, 1000);
    });
    return p; 
}

Promise.all([func(1), func(2), func(3)])
.then(function(results){
    console.log(results);
});

// console的值为 [1, 2, 3]

三个异步操作的并行执行的,等到它们都执行完后才会进到then里面,all会把所有异步操作的结果放进一个数组中传给then,就是上面的results。

Generator 函数

之前没怎么接触过,是一个可以返回多次的函数。
generator 函数由 function*定义,除了 return 还可以使用 yield 返回多次。那到底怎么用?

应用

先来个简单的例子:
斐波那契数列,一般我们要这样写:

function fib(max) {
    var
        t,
        a = 0,
        b = 1,
        arr = [0, 1];
    while (arr.length < max) {
        t = a + b;
        a = b;
        b = t;
        arr.push(t);
    }
    return arr;
}
fib(5); // [0, 1, 1, 2, 3]

使用 generator:

function* fib(max) {
    var
        t,
        a = 0,
        b = 1,
        n = 1;
    while (n < max) {
        yield a;
        t = a + b;
        a = b;
        b = t;
        n ++;
    }
    return a;
}

var f = fib(5);
f.next(); // {value: 0, done: false}
f.next(); // {value: 1, done: false}
f.next(); // {value: 1, done: false}
f.next(); // {value: 2, done: false}
f.next(); // {value: 3, done: true}

//

上一篇下一篇

猜你喜欢

热点阅读