Promise笔记
Promise笔记
执行流程
// Promise的设计
new Promise(
/* 执行器 executor */
function (resolve, reject) {
// 一段耗时很长的异步操作
resolve(); // 数据处理完成
reject(); // 数据处理出错
}
)
.then(function A() {
// 成功,下一步
}, function B() {
// 失败,做相应处理
});
支持情况
三个状态
- pending[待定] 初始状态
- fulfilled[实现] 操作成功
- rejected[被否定] 操作失败
Promise 状态一经改变,不会再边
Promise 一经创建,执行器立即执行
.then()
- 接受两个函数作为参数
fulfilled
,rejexted
- 返回一个新的Promise实例,所以可以链式调用
- 当前边的Promise状态改变时,
·then()
根据其最终的状态,选择待定状态相应函数执行 - 状态相应函数可以返回新的Promise或其他值
- 如果返回新的Promise,那么下一级的
.then()
会在新的Promise状态改变后执行 - 如果返回其他值,则会立即执行下一级的
.then()
基础例子
定时执行
new Promise( resolve => {
setTimeout( () => {
resolve('hello');
}, 2000);
})
.then( value => {
console.log( value + ' world');
});
输出 : hello world
分两次,顺序依次执行
new Promise(resolve => {
setTimeout(() => {
resolve('hello');
}, 2000);
})
.then(value => {
console.log(value);
return new Promise(resolve => {
setTimeout(() => {
resolve('world1');
}, 2000);
});
})
.then(value => {
console.log(value + ' world2');
});
输出 :
hello
world1 world2
假如一个Promise已经完成了,再.then()会怎样?
let promise = new Promise(resolve => {
setTimeout(() => {
console.log('the promise fulfilled');
resolve('hello, world');
}, 1000);
});
setTimeout(() => {
promise.then( value => {
console.log(value);
});
}, 3000);
输出 :
the promise fulfilled
hello, world
假如在.then()的函数里面不返回新的Promise,会怎样?
new Promise(resolve => {
setTimeout( () => {
resolve('hello');
}, 2000);
})
.then( value => {
console.log(value);
(function () {
return new Promise(resolve => {
setTimeout(() => {
console.log('Mr.Laurence');
resolve('Merry Xmas');
}, 2000);
});
}());
return false;
})
.then( value => {
console.log(value + ' world');
});
输出 :
hello
false world
Mr.Laurence
嵌套.then()
new Promise(resolve => {
console.log('Step 1');
setTimeout(() => {
resolve(100);
}, 1000);
})
.then(value => {
return new Promise(resolve => {
console.log('Step 1-1');
setTimeout(() => {
resolve(110);
}, 1000);
})
.then(value => {
console.log('Step 1-2');
return value;
})
.then(value => {
console.log('Step 1-3');
return value;
});
})
.then(value => {
console.log(value);
console.log('Step 2');
});
//改写
new Promise(resolve => {
console.log('Step 1');
setTimeout(() => {
resolve(100);
}, 1000);
})
.then(value => {
return new Promise(resolve => {
console.log('Step 1-1');
setTimeout(() => {
resolve(110);
}, 1000);
})
})
.then(value => {
console.log('Step 1-2');
return value;
})
.then(value => {
console.log('Step 1-3');
return value;
})
.then(value => {
console.log(value);
console.log('Step 2');
});
输出 :
Step 1
Step 1-1
Step 1-2
Step 1-3
110
Step 2
典型问题
假设doSomething和doSomethingElse返回的都是一个Promise实例
原文地址:http://pouchdb.com/2015/05/18/we-have-a-problem-with-promises.html
译文地址:http://fex.baidu.com/blog/2015/07/we-have-a-problem-with-promises/
// 问题一
doSomething()
.then(function () {
return doSomethingElse();
})
.then(finalHandler);
// 答案
// doSomething
// |-----------|
// doSomethingElse(undefined)
// |------------|
// finalHandler(resultOfDoSomethingElse)
// |------------|
// 问题二
doSomething()
.then(function () {
doSomethingElse();
})
.then(finalHandler);
// 答案
// doSomething
// |------------------|
// doSomethingElse(undefined)
// |------------------|
// finalHandler(undefined)
// |------------------|
// 问题三
doSomething()
.then(doSomethingElse())
.then(finalHandler);
// 答案
// doSomething
// |------------------|
// doSomethingElse(undefined)
// |----------------------------------|
// finalHandler(resultOfDoSomething)
// |------------------|
// 问题四
doSomething()
.then(doSomethingElse)
.then(finalHandler);
// 答案
// doSomething
// |-----------|
// doSomethingElse(resultOfDoSomething)
// |------------|
// finalHandler(resultOfDoSomethingElse)
// |------------------|
实现队列
let promise = doSomething();
promise = promise.then(doSomethingElse);
promise = promise.then(doSomethingElse2);
promise = promise.then(doSomethingElse3);
使用forEach
function queue(things) {
let promise = Promise.resolve();
things.forEach(thing => {
promise = promise.then(() => {
return new Promise(resolve => {
doThing(thing, () => {
resolve()
});
});
});
});
return promise;
}
queue(['21lots', 'of', 'things']);
常见错误 : 没有把
.then()
产生的❤新Promise实例复制给Promise,没有生成队列
使用.reduce()
错误处理
- reject('错误信息').then(null,message => {})
- throw new Error('错误信息').catch(message => {})
- 推荐使用第二种,清晰好读,并且可以捕获前面的错误
- 建议在所有队列都加上
.catch()
,以避免漏掉错误处理造成意想不到的问题
Promise会自动捕获内部异常,并交给rejected
响应函数处理-catch捕获
console.log('here we go');
new Promise( resolve => {
setTimeout( () => {
throw new Error('bye');
}, 2000);
})
.then( value => {
console.log( value + ' world');
})
.catch( error => {
console.log( 'Error:', error.message);
});
Promise会自动捕获内部异常,并交给rejected响应函数处理-reject响应捕获
new Promise( (resolve, reject) => {
setTimeout( () => {
reject('bye');
}, 2000);
})
.then( value => {
console.log( value + ' world');
}, value => {
console.log( 'Error:', value);
});
.catch() + .then() 连用
new Promise(resolve => {
setTimeout(() => {
resolve();
}, 1000);
})
.then( () => {
console.log('start');
throw new Error('test error');
})
.catch( err => {
console.log('I catch:', err);
// 下面这一行的注释将引发不同的走向
// throw new Error('another error');
})
.then( () => {
console.log('arrive here');
})
.then( () => {
console.log('... and here');
})
.catch( err => {
console.log('No, I catch:', err);
});
Promise.all()
- Promise.all([p1,p2,p3])用于将多个Promise实例包装成一个新的Promise实例
- 返回的实例就是普通的Promise
- 接受一个数组作为参数
- 数组里可以是Promise对象,也可以是别的值,只有Promise会等待状态改变
- 当所有的Promise都完成,该Promise完成,返回值是全部值的数组
- 任何一个失败,该Promise失败,返回值是第一个子Promise的结果
Promise.all([1, 2, 3])
.then( all => {
console.log('1:', all);
return Promise.all([ function () {
console.log('ooxx');
}, 'xxoo', false]);
})
.then( all => {
console.log('2:', all);
let p1 = new Promise( resolve => {
setTimeout(() => {
resolve('I\'m P1');
}, 1500);
});
let p2 = new Promise( (resolve, reject) => {
setTimeout(() => {
resolve('I\'m P2');
}, 1000);
});
let p3 = new Promise( (resolve, reject) => {
setTimeout(() => {
resolve('I\'m P3');
}, 3000);
});
return Promise.all([p1, p2, p3]);
})
.then( all => {
console.log('all', all);
})
.catch( err => {
console.log('Catch:', err);
});
Promise.all() + .map()
const fs = require('fs');
const path = require('path');
const FileSystem = require('./FileSystem');
function findLargest(dir) {
return FileSystem.readDir(dir, 'utf-8')
.then( files => {
return Promise.all( files.map( file => {
return new Promise (resolve => {
fs.stat(path.join(dir, file), (err, stat) => {
if (err) throw err;
if (stat.isDirectory()) {
return resolve({
size: 0
});
}
stat.file = file;
resolve(stat);
});
});
}));
})
.then( stats => {
let biggest = stats.reduce( (memo, stat) => {
if(memo.size < stat.size) {
return stat;
}
return memo;
});
return biggest.file;
})
}
Promise.resolve()
返回一个
fulfilled
的Promise实例,或原始的Promise实例
- 参数为空,返回一个
fulfilled
的Promise实例 - 参数是一个普通值,同上,不过
fulfilled
相应函数会得到这个参数 - 参数为Promise实例,则返回该实例,不做任何修改
- 参数为
thenable
,则立刻执行.then()
Promise.resolve()
.then( (value) => {
console.log('Step 1',value);
return Promise.resolve('Hello');
})
.then( value => {
console.log(value, 'World');
return Promise.resolve(new Promise( resolve => {
setTimeout(() => {
resolve('Good');
}, 2000);
}));
})
.then( value => {
console.log(value, ' evening');
return Promise.resolve({
then() {
console.log(', everyone');
}
})
})
输出:
Step 1 undefined
Hello World
Good evening
, everyone
Promise.reject()
返回一个
reject
的Promise实例,或原始的Promise实例
- Promise.reject()不认
thenable
- 参数为空,返回一个
fulfilled
的Promise实例 - 参数是一个普通值,同上,不过
fulfilled
相应函数会得到这个参数 - 参数为Promise实例,则返回该实例,不做任何修改
- 参数为
thenable
,则立刻执行.then()
let promise = Promise.reject('something wrong');
promise
.then( () => {
console.log('it\'s ok');
})
.catch( () => {
console.log('no, it\'s not ok');
return Promise.reject({
then() {
console.log('it will be ok');
},
catch() {
console.log('not yet');
}
});
});
Promise.race()
类似于
Promise.all()
,区别在于它有任意一个完成就算完成
常见用法
- 把异步操作和定时器放一起
- 如果定时器先触发,就认为超时,告知用户
示例
let p1 = new Promise(resolve => {
// 这是一个长时间的调用
setTimeout(() => {
resolve('I\'m P1');
}, 10000);
});
let p2 = new Promise(resolve => {
// 这是个稍短的调用
setTimeout(() => {
resolve('I\'m P2');
}, 2000)
});
Promise.race([p1, p2])
.then(value => {
console.log(value);
});
把回调包装成Promise
- 可读性更好
- 返回结果可以加入任何Promise队列
const mysql = mysql = require('mysql');
const sql = function(sql, arr) {
return new Promise(resolve => {
connection.query(sql, arr, function(err, rows) {
if (err) {
throw err;
}
resolve(rows);
})
})
}
new Promise(resolve => {
ttt('SELECT * FROM `user` WHERE `username` = ?', req.query.name).then(value => {
arr.user = value[0];
resolve(value[0].id)
})
})
test.then(value => {
return new Promise(resolve => {
ttt('SELECT * FROM `order` WHERE `userid` = ?', value).then(value => {
resolve(value[0].id)
})
})
})
把任何异步操作包装成Promise
// 弹出窗体
let confirm = popupManager.confirm('您确定么?');
confirm.promise
.then(() => {
// do confirm staff
})
.catch(() => {
// do cancel staff
});
// 窗体的构造函数
class Confirm {
constructor() {
this.promise = new Promise( (resolve, reject) => {
this.confirmButton.onClick = resolve;
this.cancelButton.onClick = reject;
})
}
}
Fetch API
fetch('some.json')
.then( response => {
return response.json();
})
.then( json => {
// do something with the json
})
.catch( err => {
console.log(err);
});
async/await
- 赋予
javascript
以顺序手法编写异步脚本的能力 - 既保留异步运算符的无阻塞特性,还继续使用同步写法
- 还能正常使用
return
/try
/catch
- 基于Promise
function resolveAfter2Seconds(x) {
return new Promise(resolve => {
setTimeout(() => {
resolve(x);
}, 2000);
});
}
async function f1() {
var x = await resolveAfter2Seconds(10);
console.log(x); // 10
}
f1();
异步操作
常见异步操作
// 异步操作的常见语法
// https://www.imooc.com/video/16610
// 事件侦听与响应
document.getElementById('start').addEventListener('click', start, false);
function start() {
// 响应事件,进行相应的操作
}
// jQuery 用 `.on()` 也是事件侦听
$('#start').on('click', start);
### 常见异步操作解决方案
// 回调
// 比较常见的有ajax
$.ajax('http://baidu.com', {
success: function (res) {
// 这里就是回调函数了
}
});
// 或者在页面加载完毕后回调
$(function(){
// 这里也是回调函数
});
回调地狱
// 回调地狱
// https://www.imooc.com/video/16611
a(function (resultsFromA) {
b(resultsFromA, function (resultsFromB) {
c(resultsFromB, function (resultsFromC) {
d(resultsFromC, function (resultsFromD) {
e(resultsFromD, function (resultsFromE) {
f(resultsFromE, function (resultsFromF) {
console.log(resultsFromF);
})
})
})
})
})
});
回调地狱示例
// 遍历目录,找出最大的一个文件
// https://www.imooc.com/video/16611
const fs = require('fs');
const path = require('path');
function findLargest(dir, callback) {
fs.readdir(dir, function (err, files) {
if (err) return callback(err); // [1]
let count = files.length; // [2]
let errored = false;
let stats = [];
files.forEach( file => {
fs.stat(path.join(dir, file), (err, stat) => {
if (errored) return; // [1]
if (err) {
errored = true;
return callback(err);
}
stats.push(stat); // [2]
if (--count === 0) {
let largest = stats
.filter(function (stat) { return stat.isFile(); })
.reduce(function (prev, next) {
if (prev.size > next.size) return prev;
return next;
});
callback(null, files[stats.indexOf(largest)]);
}
});
});
});
}
findLargest('./path/to/dir', function (err, filename) {
if (err) return console.error(err);
console.log('largest file was:', filename);
});
// Promise的设计
// https://www.imooc.com/video/16612
new Promise(
/* 执行器 executor */
function (resolve, reject) {
// 一段耗时很长的异步操作
resolve(); // 数据处理完成
reject(); // 数据处理出错
}
)
.then(function A() {
// 成功,下一步
}, function B() {
// 失败,做相应处理
});
// 简单的范例-定时执行
// https://www.imooc.com/video/16613
console.log('here we go');
new Promise( resolve => {
setTimeout( () => {
resolve('hello');
}, 2000);
})
.then( value => {
console.log( value + ' world');
});
// 分两次,顺序依次执行
// https://www.imooc.com/video/16614
console.log('here we go');
new Promise( resolve => {
setTimeout( () => {
resolve('hello');
}, 2000);
})
.then( value => {
console.log(value);
return new Promise( resolve => {
setTimeout( () => {
resolve('world');
}, 2000);
});
})
.then( value => {
console.log( value + ' world');
});
// 假如一个Promise已经完成了,再.then()会怎样?
// https://www.imooc.com/video/16615
console.log('start');
let promise = new Promise(resolve => {
setTimeout(() => {
console.log('the promise fulfilled');
resolve('hello, world');
}, 1000);
});
setTimeout(() => {
promise.then( value => {
console.log(value);
});
}, 3000);
// 假如在.then()的函数里面不返回新的Promise,会怎样?
// https://www.imooc.com/video/16616
console.log('here we go');
new Promise(resolve => {
setTimeout( () => {
resolve('hello');
}, 2000);
})
.then( value => {
console.log(value);
console.log('everyone');
(function () {
return new Promise(resolve => {
setTimeout(() => {
console.log('Mr.Laurence');
resolve('Merry Xmas');
}, 2000);
});
}());
return false;
})
.then( value => {
console.log(value + ' world');
});
// 嵌套.then()
// https://www.imooc.com/video/16618
console.log('start');
new Promise( resolve => {
console.log('Step 1');
setTimeout(() => {
resolve(100);
}, 1000);
})
.then( value => {
return new Promise(resolve => {
console.log('Step 1-1');
setTimeout(() => {
resolve(110);
}, 1000);
})
.then( value => {
console.log('Step 1-2');
return value;
})
.then( value => {
console.log('Step 1-3');
return value;
});
})
.then(value => {
console.log(value);
console.log('Step 2');
});
// 随堂小测试
// https://www.imooc.com/video/16619
// 假设doSomething和doSomethingElse返回的都是一个Promise实例
// 原问题地址:http://pouchdb.com/2015/05/18/we-have-a-problem-with-promises.html
// 译文地址:http://fex.baidu.com/blog/2015/07/we-have-a-problem-with-promises/
// 问题一
doSomething()
.then(function () {
return doSomethingElse();
})
.then(finalHandler);
// 答案
// doSomething
// |-----------|
// doSomethingElse(undefined)
// |------------|
// finalHandler(resultOfDoSomethingElse)
// |------------|
// 问题二
doSomething()
.then(function () {
doSomethingElse();
})
.then(finalHandler);
// 答案
// doSomething
// |------------------|
// doSomethingElse(undefined)
// |------------------|
// finalHandler(undefined)
// |------------------|
// 问题三
doSomething()
.then(doSomethingElse())
.then(finalHandler);
// 答案
// doSomething
// |------------------|
// doSomethingElse(undefined)
// |----------------------------------|
// finalHandler(resultOfDoSomething)
// |------------------|
// 问题四
doSomething()
.then(doSomethingElse)
.then(finalHandler);
// 答案
// doSomething
// |-----------|
// doSomethingElse(resultOfDoSomething)
// |------------|
// finalHandler(resultOfDoSomethingElse)
// |------------------|
// Promise会自动捕获内部异常,并交给rejected响应函数处理-catch捕获
// https://www.imooc.com/video/16620
console.log('here we go');
new Promise( resolve => {
setTimeout( () => {
throw new Error('bye');
}, 2000);
})
.then( value => {
console.log( value + ' world');
})
.catch( error => {
console.log( 'Error:', error.message);
});
// Promise会自动捕获内部异常,并交给rejected响应函数处理-reject响应捕获
// https://www.imooc.com/video/16620
console.log('here we go');
new Promise( (resolve, reject) => {
setTimeout( () => {
reject('bye');
}, 2000);
})
.then( value => {
console.log( value + ' world');
}, value => {
console.log( 'Error:', value);
});
// .catch() + .then()
// https://www.imooc.com/video/16621
console.log('here we go');
new Promise(resolve => {
setTimeout(() => {
resolve();
}, 1000);
})
.then( () => {
console.log('start');
throw new Error('test error');
})
.catch( err => {
console.log('I catch:', err);
// 下面这一行的注释将引发不同的走向
// throw new Error('another error');
})
.then( () => {
console.log('arrive here');
})
.then( () => {
console.log('... and here');
})
.catch( err => {
console.log('No, I catch:', err);
});
// 使用`Promise.all()`包装多个Promise实例
// https://www.imooc.com/video/16634
console.log('here we go');
Promise.all([1, 2, 3])
.then( all => {
console.log('1:', all);
return Promise.all([ function () {
console.log('ooxx');
}, 'xxoo', false]);
})
.then( all => {
console.log('2:', all);
let p1 = new Promise( resolve => {
setTimeout(() => {
resolve('I\'m P1');
}, 1500);
});
let p2 = new Promise( (resolve, reject) => {
setTimeout(() => {
resolve('I\'m P2');
}, 1000);
});
let p3 = new Promise( (resolve, reject) => {
setTimeout(() => {
resolve('I\'m P3');
}, 3000);
});
return Promise.all([p1, p2, p3]);
})
.then( all => {
console.log('all', all);
})
.catch( err => {
console.log('Catch:', err);
});
// 遍历目录,找出最大的一个文件-通过Promise.all()和.map()
// https://www.imooc.com/video/16622
const fs = require('fs');
const path = require('path');
const FileSystem = require('./FileSystem');
function findLargest(dir) {
return FileSystem.readDir(dir, 'utf-8')
.then( files => {
return Promise.all( files.map( file => {
return new Promise (resolve => {
fs.stat(path.join(dir, file), (err, stat) => {
if (err) throw err;
if (stat.isDirectory()) {
return resolve({
size: 0
});
}
stat.file = file;
resolve(stat);
});
});
}));
})
.then( stats => {
let biggest = stats.reduce( (memo, stat) => {
if(memo.size < stat.size) {
return stat;
}
return memo;
});
return biggest.file;
})
}
// 开发一个爬虫,爬取某网站。(半成品)
// https://www.imooc.com/video/16624
let url = ['http://blog.meathill.com/'];
function fetchAll(urls) {
return urls.reduce((promise, url) => {
return promise.then( () => {
return fetch(url);
});
}, Promise.resolve());
}
function fetch(url) {
return spider.fetch(url)
.then( content => {
return saveOrOther(content);
})
.then( content => {
let links = spider.findLinks(content);
return fetchAll(links);
});
}
fetchAll(url);
// Promise.resolve()
// https://www.imooc.com/video/16625
console.log('start');
Promise.resolve()
.then( () => {
console.log('Step 1');
return Promise.resolve('Hello');
})
.then( value => {
console.log(value, 'World');
return Promise.resolve(new Promise( resolve => {
setTimeout(() => {
resolve('Good');
}, 2000);
}));
})
.then( value => {
console.log(value, ' evening');
return Promise.resolve({
then() {
console.log(', everyone');
}
})
})
// Promise.reject()
// https://www.imooc.com/video/16626
let promise = Promise.reject('something wrong');
promise
.then( () => {
console.log('it\'s ok');
})
.catch( () => {
console.log('no, it\'s not ok');
return Promise.reject({
then() {
console.log('it will be ok');
},
catch() {
console.log('not yet');
}
});
});
// Promise.race()
// https://www.imooc.com/video/16627
console.log('start');
let p1 = new Promise(resolve => {
// 这是一个长时间的调用
setTimeout(() => {
resolve('I\'m P1');
}, 10000);
});
let p2 = new Promise(resolve => {
// 这是个稍短的调用
setTimeout(() => {
resolve('I\'m P2');
}, 2000)
});
Promise.race([p1, p2])
.then(value => {
console.log(value);
});
// 把回调包装成Promise
// https://www.imooc.com/video/16628
const fs = require('./FileSystem');
fs.readFile('../README.md', 'utf-8')
.then(content => {
console.log(content);
});
// 使用JS包装readFile
// https://www.imooc.com/video/16628
const fs = require('fs');
module.exports = {
readDir: function (path, options) {
return new Promise( resolve => {
fs.readdir(path, options, (err, files) => {
if (err) {
throw err;
}
resolve(files);
});
});
},
readFile: function (path, options) {
return new Promise( resolve => {
fs.readFile(path, options, (err, content) => {
if (err) {
throw err;
}
resolve(content);
});
});
}
};
// 把任何异步操作包装成Promise
// https://www.imooc.com/video/16629
// 弹出窗体
let confirm = popupManager.confirm('您确定么?');
confirm.promise
.then(() => {
// do confirm staff
})
.catch(() => {
// do cancel staff
});
// 窗体的构造函数
class Confirm {
constructor() {
this.promise = new Promise( (resolve, reject) => {
this.confirmButton.onClick = resolve;
this.cancelButton.onClick = reject;
})
}
}
// Fetch API
// https://www.imooc.com/video/16630
fetch('some.json')
.then( response => {
return response.json();
})
.then( json => {
// do something with the json
})
.catch( err => {
console.log(err);
});
// async/await
// https://www.imooc.com/video/16631
function resolveAfter2Seconds(x) {
return new Promise(resolve => {
setTimeout(() => {
resolve(x);
}, 2000);
});
}
async function f1() {
var x = await resolveAfter2Seconds(10);
console.log(x); // 10
}
f1();