初始Promise/A+规范

1,192 阅读7分钟

view继承关系

个人博客: http://zhangsunyucong.top

前言

这篇文章主要讲两个内容,一是,初步认识Promise,二是,Async模块和Async/Await的使用

什么是Promise

Promise表示一个异步操作的最终结果。一个Promise对象有一个then方法,then方法中返回一个Promise。

相关的概念

  • promise是一个包含了兼容promise规范then方法的对象或函数,
  • thenable 是一个包含了then方法的对象或函数。
  • value 是任何Javascript值。 (包括 undefined, thenable, promise等).
  • exception 是由throw表达式抛出来的值。
  • reason 是一个用于描述Promise被拒绝原因的值。

Promise的状态

Promise有三种状态:pending, fulfilled 或 rejected。pending是等待执行状态,fulfilled是成功执行状态,rejected是失败执行状态。

Promise只能从pending到fulfilled或者从pending到rejected状态,当状态发生改变时,promise.then(onFulfilled, onRejected)方法将被调用。Promise可以使用resolve或者reject将value或者reason作为下一个Promise的第一个回调参数。

来个简单的Promise基本用法:

var promise = new Promise(function(resolve, reject){
    //do something
    if(success){
        resolve(value);
    } else {
        reject(value);
    }
});

promise.then(function(value){
    //成功时调用
}, function(value){
    //失败时调用
});

上面的代码只是表示Promise用法的流程.

使用Promise/A+规范实现以下几个功能

  • 上一步的结果可以作为下一步的参数
  • 出现异常时,能够捕获到异常
  • 可以在每一步进行流程控制

Promise的具体知识,可以参考这里

下面介绍Async模块和ES7的Async/Await的使用

Async模块

Async模块的github地址:github.com/caolan/asyn…

配置好node的环境后(具体过程,自己百度),安装Async模块

npm install --save async

Async模块提供了很多关于集合,流程控制,工具方法,这里只体验几个常见的流程控制方法:series,parallel,waterfall,auto。其他方法的用法,可以查看官方文档:文档地址

series的使用

series(tasks, callback)

tasks可以是数组或者对象

series是串行执行tasks中的任务,如果有一个任务执行返回了错误信息,则不再继续执行后面未执行的任务,并将结果以数组或者对象的形式传给callback。具体结果的格式由你定义tasks时使用的是数组还是对象。

    async.series([
        function (callback) {
            setTimeout(function () {
                console.log("one");
                callback(null, 'one');
            }, 300);
        },
        function (callback) {
            setTimeout(function () {
                console.log("two");
                callback(null, 'two');
            }, 200);
        },
        function (callback) {
            setTimeout(function () {
                console.log("three");
                callback(null, 'three');
            }, 100);
        }
    ], function (err, results) {
        console.log(err);
        console.log(results);
    });

运行结果:

one two three null [ 'one', 'two', 'three' ]

当tasks是对象:

async.series({
        one: function (callback) {
            setTimeout(function () {
                console.log("one");
                callback(null, 'one');
            }, 300);
        },
        two: function (callback) {
            setTimeout(function () {
                console.log("two");
                callback(null, 'two');
            }, 200);
        },
        three: function (callback) {
            setTimeout(function () {
                console.log("three");
                callback(null, 'three');
            }, 100);
        }
    }, function (err, results) {
        if(err) {
            console.log("异常结束" + '结果为:' + results);
            return;
        }
        console.log(results);
    });

运行结果:

one two three { one: 'one', two: 'two', three: 'three' }

上面代码中,从上到下的函数开始执行时间是逐渐减小的,而运行结果的输出顺序是one,two,three,说明series是串行执行任务的。

将第二个任务的代码改为以下的样子:

function (callback) {
    console.log("two");
    setTimeout(function () {
        callback("errMsg", 'two');
    }, 200);
}

运行的结果为:

one two errMsg [ 'one', 'two' ]

可以看到,当第二个任务返回了错误信息,则不会再继续执行后面未执行的任务

parallel的使用

parallel(tasks, callback)

tasks可以是一个数组或者对象

parallel是并行执行多个任务,如果有一个任务执行返回了一个错误信息,则不再继续执行后面未执行的任务,并将结果以数组或者对象的形式传给callback。

async.parallel([
        function (callback) {
            setTimeout(function() {
                console.log('one');
                callback(null, 'one');
            }, 500);
        },
        function (callback) {
            setTimeout(function() {
                console.log('two');
                callback(null, 'two');
            }, 200);
        },
        function (callback) {
            setTimeout(function() {
                console.log('three');
                callback(null, 'three');
            }, 100);
        }
    ], function (err, results) {
        console.log(err);
        console.log(results);
    });

运行结果为:

three two one null [ 'one', 'two', 'three' ]

结果中的输出顺序是three,two,one,说明parallel是并行执行任务的。

同样,将第二个任务的代码改为:(数组定义tasks)

function (callback) {
    setTimeout(function() {
        console.log('two');
        callback("errMsg", 'two');
    }, 200);
},

运行的结果为:

three two errMsg [ <1 empty item>, 'two', 'three' ] one


将第二个任务代码改为:(数组定义tasks)

function (callback) {
    setTimeout(function() {
        console.log('two');
        callback("errMsg", 'two');
    }, 200);
},

将第三个任务代码改为:(数组定义tasks)

function (callback) {
    setTimeout(function() {
        console.log('three');
        callback(null, 'three');
    }, 200);
}

也就是,第三个的开始执行时间改成和出现错误信息的第二个任务的时间一样。

运行的结果为:

two errMsg [ <1 empty item>, 'two' ] three one

从结果中可以看出,当前面执行的未完成的任务会占一个位置,而后面未完成的任务不会占数组的位置。

parallelLimit(tasks, limit, callback)

parallelLimit和parallel差不多,区别是它可以指定同时并行执行任务的最大数量。

    async.parallelLimit({
        one: function (callback) {
            setTimeout(function() {
                console.log('one');
                callback(null, 'one');
            }, 200);
        },
        two: function (callback) {
            setTimeout(function() {
                console.log('two');
                callback(error, 'two');
            }, 200);
        },
        three: function (callback) {
            setTimeout(function() {
                console.log('three');
                callback(null, 'three');
            }, 100);
        }
    }, 2, function (err, results) {
        console.log(err);
        console.log(results);
    });

运行的结果为:

one two errMsg { one: 'one', two: 'two' } three

如果是tasks是数组时,运行的结果是:

two errMsg [ <1 empty item>, 'two' ] one

由于同时并行执行任务的最大数量是2,由于第二个任务产生错误信息,第三个任务还没开始执行。另外如果要取最后回调结果中的值,对象定义tasks可能会更好。

waterfall的使用

waterfall(tasks, callback)

tasks只能是数组类型

waterfall会串行执行tasks中的任务,前一个任务的结果可以作为下一个任务的参数。

    async.waterfall([
        function (callback) {
            console.log("one");
            setTimeout(function() {
                callback(null, 'one', 'two');
            }, 200);
        },
        function (arg1, arg2, callback) {
            console.log("two" + '参数:' + "arg1是" + arg1 + "arg2是" + arg2);
            setTimeout(function() {
                callback(null, 'three');
            }, 200);
        },
        function (arg3, callback) {
            console.log("three" + '参数:' + "arg3是" + arg3);
            setTimeout(function() {
                callback(null, 'done', 'done1');
            }, 200);
        }
    ], function (err, results) {
        if(err) {
            console.log("异常结束" + '结果为:' + results);
            return;
        }
        console.log(results);
    });

运行的结果是:

one two参数:arg1是onearg2是two three参数:arg3是three done

输出的结果的顺序是one,two, three,是串行执行的。前一个任务的结果可以作为下一个任务的参数。

注意一下,代码中控制台输出的one,two,three代码是移到了定时器的外面。

auto的使用

auto(tasks, concurrencyopt, callback)

auto可以串行和并行执行任务,可以定义任务之间的依赖关系。没有依赖关系的任务会尽可能快的开始并行执行,串行是由于任务的依赖关系而实现的。concurrencyopt指定的是并行执行任务的最大数量。tasks只能是对象类型。

    async.auto({
        task1: function(callback) {
            setTimeout(function() {
                console.log('task1');
                callback(null, 'data', 'data1');
            }, 200);
        },
        task2: function(callback) {
            setTimeout(function () {
                console.log('task2');
                callback(null, 'data2');
            }, 100)
        },
        task3: ['task1', 'task2', function(results, callback) {
            console.log('task3', JSON.stringify(results));
            setTimeout(function () {
                console.log('task3');
                callback(null, 'data3');
            }, 200);

        }],
        task4: ['task3', function(results, callback) {
            console.log('task4', JSON.stringify(results));
            setTimeout(function () {
                console.log('task4');
                callback(null, {'task2':results.task2, 'task4':'data4'});
            }, 100);

        }]
    }, function(err, results) {
        console.log('err = ', err);
        console.log('results = ', results);
    });

运行的结果是:

task2 task1 task3 {"task2":"data2","task1":["data","data1"]} task3 task4 {"task2":"data2","task1":["data","data1"],"task3":"data3"} task4 err = null results = { task2: 'data2', task1: [ 'data', 'data1' ], task3: 'data3', task4: { task2: 'data2', task4: 'data4' } }

task1和task2是不依赖于任何其他任务的,它们会尽可能的开始,而且由于它们是并行执行的,task2的开始时间较短,所以task2比task1先开始。task3依赖于task1和task2,所以task3等到task1和task2执行完毕后再执行。task4依赖task3,所以task4要等到task3执行完毕后再执行。

ES7的Async/Await

主要看它们的用法

var task1 = function () {
    return new Promise(function (resolve, reject) {
        setTimeout(function () {
            resolve("result");
        }, 1000);
    });
};

var excTask1 = async function () {
    console.log("start");
    console.log(await task1());
    console.log("end");
};

function awaitDemo() {
    excTask1();
}

连续点击运行四次,运行的结果为:

start start start start result end result end result end result end

async代表是一个async函数,await只能用在async函数中,await等待一个Promise的返回,直到Promise返回了才会继续执行await后面的代码。这里的Promise利用setTimeout模拟异步任务。

从输出结果中可以看出,每次执行都是到await时,就停止等待Promise的返回,后再继续执行await后面的代码。