理解ES6的异步方案Promise/Generator/asyn

2017-09-06  本文已影响0人  哎嘿沁

异步

回想在ES6没出现之前,需要异步处理事件的时候大多数情况下就是回调函数,可能还是是一层一层的callback嵌套,代码混乱逻辑不清晰,如果还有错误处理,那代码就更加冗杂让人不想去读第二遍!

回调

通过函数参数传递到其他代码里的,某一块可以执行的引用。

容易引发的问题

1.信任问题
2.同步or异步 不确定?

Promise对象

所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。

构建Promise对象
var promise = new Promise(function(resolve, reject) {
  // ... some code
  if (/* 异步操作成功 */){
    resolve(value);
  } 
  else {
    reject(error);
  }
});

promise的构造函数传入一个回调,该回调接受两个回调函数resolve和reject。
resolve函数的作用是,将Promise对象的状态从“未完成”变为“成功”(即从 pending 变为 resolved),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;reject函数的作用是,将Promise对象的状态从“未完成”变为“失败”(即从 pending 变为 rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。

Promise实例生成以后,可以用then方法分别指定resolved状态和rejected状态的回调函数。

promise.then(function(value) {
  // success
}, function(error) {
  // failure
});

Promise 提供then方法加载回调函数,catch方法捕捉执行过程中抛出的错误。

Promise.resovle方法,可以将不是Promise对象作为参数,返回一个Promise对象。
有两种情形:
1.假设传入的参数没有一个.then方法,那么这个返回的Promise对象变成了resolve状态,其resolve的值就是这个对象本身。
2.假设传入的参数带有一个then方法(称为thenable对象), 那么将这个对象的类型变为Promise,其then方法变成Promise.prototype.then方法。

不管是then还是catch方法调用,都返回一个新的promise对象;其实then方法的调用,只不过是注册了完成态和失败态下的回调函数而已。这些回调函数组成一个回调队列,处理resolve的值。

Q 链式调用的then和非链式调用的有什么区别?
var promise1 = new Promise(function(resolve){
    resolve(1);
});
var thenPromise = promise1.then(function(value){
    console.log(value);
});
var catchPromise = thenPromise.catch(function(error){
    console.log(error);
});
console.log(promise1 !== thenPromise); // true
console.log(thenPromise !== catchPromise); //true

情况1

var promise1 = new Promise(function(resolve){
    resolve(1);
});
promise1.then(function(value){
    return value * 2;
});
promise1.then(function(value){
    return value * 2;
});
promise1.then(function(value){
    console.log("1"+value);
});

情况2

var promise1 = new Promise(function(resolve){
    resolve(2);
});
promise1.then(function(value){
    return value * 2;
}).then(function(value){
    return value * 2;
}).then(function(value){
    console.log("1"+value);
});

Generator 函数

整个 Generator 函数就是一个异步任务的容器。

Generator 函数除了状态机,还是一个遍历器对象生成函数。返回的遍历器对象,可以依次遍历 Generator 函数内部的每一个状态。

function* gen(x) {
  var y = yield x + 2;
  return y;
}

var g = gen(1);
g.next() // { value: 3, done: false }
g.next() // { value: undefined, done: true }
  • Generator 函数的一个重要实际意义就是用来处理异步操作,改写回调函数。
  • Generator 函数可以暂停执行和恢复执行,这是它能封装异步任务的根本原因。
  • 除此之外,它还有两个特性,使它可以作为异步编程的完整解决方案:函数体内外的数据交换和错误处理机制。
function* asyncJob(){

// ...其他代码

varf=yieldreadFile(fileA);

// ...其他代码

}

上面代码的函数asyncJob是一个协程,它的奥妙就在其中的yield命令。它表示执行到此处,执行权将交给其他协程。
协程遇到yield命令就暂停,等到执行权返回,再从暂停的地方继续往后执行。
它的最大优点,就是代码的写法非常像同步操作。

async函数

async函数就是将 Generator 函数的星号(*)替换成async,将yield替换成await
async告诉这个函数里面有await

async function getTitle(url) { 
  let response = await fetch(url); 
  let html = await response.text(); 
  return html.match(/<title>([\s\S]+)<\/title>/i)[1];
}
getTitle('https://tc39.github.io/ecma262/').then(console.log)
await拿到的东西就是promise resolve之后的值

async 函数的实现原理,就是将 Generator 函数和自动执行器,包装在一个函数里。

function spawn(genF) {
    return new Promise(function(resolve, reject) {
        var gen = genF();
        function step(nextF) {
            try {
                var next = nextF();
            } catch (e) {
                return reject(e);
            }
            if (next.done) {
                return resolve(next.value);
            }
            Promise.resolve(next.value).then(function(v) {
                step(function() { return gen.next(v); });
            }, function(e) {
                step(function() { return gen.throw(e); });
            });
        }
        step(function() { return gen.next(undefined); });
    });
}

比较

一个异步展示动画的程序

// Promise写法
function chainAnimationPromise(elem, animations) {
    var ret = null;
    var p = Promise.resolve();

    for (var anim of animations) {
        p = p.then(function(val) {
            ret = val;
            return anim(elem);
        });
    }

    return p.catch(function(e) {

    }).then(function() {
        return ret;
    });
}
// Generator写法
function chainAnimationPromise(elem, animations) {
    return spawn(function* () {
        var ret = null;
        try {
            for (var anim of animations) {
                ret = yield anim(elem);
            }
        } catch (error) {
            
        }
        return ret;
    });
}
// async写法
async function chainAnimationPromise(elem, animations) {
    var ret = null;
    try {
        for(var anim of animations) {
            ret = await anim(elem);
        }
    } catch (error) {
        
    }
    return ret;
}
上一篇 下一篇

猜你喜欢

热点阅读