ES6 中的Promise学习笔记

在学习这个之前,我们要知道javascript的特性及短处:在JavaScript的世界中,所有代码都是单线程执行的。

由于这个“缺陷”,导致JavaScript的所有网络操作,浏览器事件,都必须是异步执行。异步执行可以用回调函数实现:

function callback() {
   console.log('Done');
}
console.log('before setTimeout()');
setTimeout(callback, 1000);    // 1秒钟后调用callback函数
console.log('after setTimeout()');

异步执行的例子
可见,异步操作会在将来的某个时间点触发一个函数调用。
AJAX就是典型的异步操作。以上一节的代码为例:

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

优化一下:先统一执行AJAX逻辑,不关心如何处理结果,然后,根据结果是成功还是失败,在将来的某个时候调用success函数或fail函数。

var ajax = ajaxGet('http://...');
ajax.ifSuccess(success)
    .ifFail(fail);

“君子一诺千金”这种“承诺将来会执行”的对象在JavaScript中称为Promise对象。
Promise有各种开源实现,在ES6中被统一规范,由浏览器直接支持。先测试一下你的浏览器是否支持Promise:

'use strict';
new Promise(function () {});
console.log('支持Promise!');     //在此会打印:支持Promise! 

来一个小例子:生成一个0-2之间的随机数,如果小于1,则等待一段时间后返回成功,否则返回失败:

function test(resolve, reject) {
    var timeOut = Math.random() * 2;
    log('set timeout to: ' + timeOut + ' seconds.');
    setTimeout(function () {
        if (timeOut < 1) {
            console.log('call resolve()...');
            resolve('200 OK');
        }
        else {
            console.log('call reject()...');
            reject('timeout in ' + timeOut + ' seconds.');
        }
    }, timeOut * 1000);
}
//这个test()函数有两个参数,这两个参数都是函数,如果执行成功,我们将调用resolve('200 OK'),如果执行失败,我们将调用reject()。
//可以看出,test()函数只关心自身的逻辑,并不关心具体的resolve和reject将如何处理结果。
//有了执行函数,我们就可以用一个Promise对象来执行它,并在将来某个时刻获得成功或失败的结果:
var p1 = new Promise(test);
var p2 = p1.then(function (result) {
    console.log('成功:' + result);
});
var p3 = p2.catch(function (reason) {
    console.log('失败:' + reason);
});

变量p1是一个Promise对象,它负责执行test函数。由于test函数在内部是异步执行的,当test函数执行成功时,执行这个函数:

p1.then(function (result) {
    console.log('成功:' + result);
});

失败时,执行这个:

var p3 = p2.catch(function (reason) {
    console.log('失败:' + reason);
});

简化代码:

new Promise(test).then(function (result) {
    console.log('成功:' + result);
}).catch(function (reason) {
    console.log('失败:' + reason);
});

Promise是一个构造函数,自己身上有allrejectresolve这几个眼熟的方法,原型上有thencatch等同样很眼熟的方法。
Promise最大的好处是在异步执行的流程中,把执行代码和处理结果的代码清晰地分离了:
promise的优势
在此then里面的函数就跟我们平时的回调函数一个意思,能够在test这个异步任务执行完成之后被执行。把原来的回调写法分离出来,在异步操作执行完后,用链式调用的方式执行回调函数。
我们知道Promise对象除了then方法,还有一个catch方法,它是做什么用的呢?其实它和then的第二个参数一样,用来指定reject的回调

Promise还可以做更多的事情,比如,有若干个异步任务,需要先做任务1,如果成功后再做任务2,任何任务失败则不再继续并执行错误处理函数。

要串行执行这样的异步任务,不用Promise需要写一层一层的嵌套代码。有了Promise,我们只需要简单地写:

job1.then(job2).then(job3).catch(handleError);

其中,job1、job2和job3都是Promise对象。
在本地写了一个demo,看看是如何串行执行,需要异步计算来获得结果:

var logging = document.getElementById('test-promise-log');
	while (logging.children.length > 1) {
	    logging.removeChild(logging.children[logging.children.length - 1]);
	}

	function log(s) {
	    var p = document.createElement('p');
	    p.innerHTML = s;
	    logging.appendChild(p);
	}

	// 0.5秒后返回input*input的计算结果:
	function multiply(input) {
	    return new Promise(function (resolve, reject) {
	        console.log('calculating第一个值 ' + input + ' x ' + input + '...');
	        setTimeout(resolve, 500, input * input);
	    });
	}

	// 0.5秒后返回input+input的计算结果:
	function add(input) {
	    return new Promise(function (resolve, reject) {
	        console.log('calculating 第二个' + input + ' + ' + input + '...');
	        setTimeout(resolve, 500, input + input);
	    });
	}

	var p = new Promise(function (resolve, reject) {
	    console.log('start new Promise...');
	    resolve(123);
	});

	p.then(multiply)
	 .then(add)
	 .then(multiply)
	 .then(add)
	 .then(function (result) {
	    console.log('Got value: ' + result);
	});

运行结果如下:
得到如下结果
除了串行执行若干异步任务外,Promise还可以并行执行异步任务。

试想一个页面聊天系统,我们需要从两个不同的URL分别获得用户的个人信息和好友列表,这两个任务是可以并行执行的,用Promise.all()实现如下:

var p1 = new Promise(function (resolve, reject) {
    setTimeout(resolve, 500, 'P1');
});
var p2 = new Promise(function (resolve, reject) {
    setTimeout(resolve, 600, 'P2');
});
// 同时执行p1和p2,并在它们都完成后执行then:
Promise.all([p1, p2]).then(function (results) {
    console.log(results); // 获得一个Array: ['P1', 'P2']
});

有些时候,多个异步任务是为了容错。比如,同时向两个URL读取用户的个人信息,只需要获得先返回的结果即可。这种情况下,用Promise.race()实现:

var p1 = new Promise(function (resolve, reject) {
    setTimeout(resolve, 500, 'P1');
});
var p2 = new Promise(function (resolve, reject) {
    setTimeout(resolve, 600, 'P2');
});
Promise.race([p1, p2]).then(function (result) {
    console.log(result); // 'P1'
});

由于p1执行较快,Promise的then()将获得结果’P1’。p2仍在继续执行,但执行结果将被丢弃。

如果我们组合使用Promise,就可以把很多异步任务以并行和串行的方式组合起来执行。

参考:https://www.liaoxuefeng.com/wiki/001434446689867b27157e896e74d51a89c25cc8b43bdb3000/0014345008539155e93fc16046d4bb7854943814c4f9dc2000
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises

猜你喜欢

转载自blog.csdn.net/zoepriselife316/article/details/85693026