了解promise诞生的历史背景
1.词语本意解释
promise [ˈprɑ:mɪs] 许诺;允诺;(有可能)
从字面意思,表示一个将来的状态。
用在异步操作里面,帮我们去处理一件未来可能发生的一件什么事。
2.MDN解释
- promise对象用于异步计算
- 一个promise表示一个现在、将来或永远不可能可用的值。
3.按照用途来解释
- 主要用来异步计算
- 可以将异步操作队列化,按照期望的顺序执行,返回符合预期的结果。
- 可以在对象之间传递和操作promise,帮助我们处理队列。
4.为什么会有Promise?
- JavaScript包含大量异步操作
- JavaScript为检查表单而生。
- 创造它的首要目标是操作DOM
- 所以,JavaScript的操作大多是异步的。
5.为什么异步操作可以避免界面冻结呢?
- 假设你去到一家饭店,自己找座坐下了,然后招呼服务员拿菜单。
- 服务员说:“对不起,我是同步服务员,我要服务完这张桌子才能招呼你。”
- 你是不是很想抽ta?
- 那一桌人明明已经吃上了,你只是想要菜单,这么小的一个动作,服务员却要你等到别人的一个大动作完成。
- 这就是同步的问题: 顺序交付的工作1234,必须按照1234的顺序完成。
- 异步,则是将耗时很长的A交付的工作交给系统之后,就去继续做B交付的工作。等到系统完成前面的工作之后,再通过回调或者事件,继续做A剩下的工作。
- 从观察者的角度看起来,AB工作的完成顺序,和交付他们的时间顺序无关,所以叫"异步"。
6.异步操作的常见语法
- 事件侦听与响应
document.getElementById('start').addEventListener('click', start, false);
function start() {
// 响应事件,进行相应的操作
}
// jQuery 用 `.on()` 也是事件侦听
$('#start').on('click', start);
- 回调
// 比较常见的有ajax
$.ajax('http://baidu.com', {
success: function (res) {
// 这里就是回调函数了
}
});
// 或者在页面加载完毕后回调
$(function(){
// 这里也是回调函数
});
7.浏览器中的JavaScript
- 异步操作以事件为主,如ready、onload
- 回调主要出现在Ajax和file API
这个时候问题尚不算严重
8.有了Node.js之后
对异步的依赖进一步加剧了...
Node.js出现了的时候,PHP、JAVA、Python这些服务器端的语言都已经很成熟了。Node.js作为一个后来者,想要从中分一杯羹,必须有自己的绝活。
- 无阻塞高并发,是Node.js的招牌
- 异步操作是其保障。
- 大量操作依赖回调函数。
9.使用Node.js开发时的问题
- 不好维护
稍有不慎,就会踏入“回调地狱”
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);
})
})
})
})
})
});
- 除此之外,还有更深层次的问题
遍历目录,找出最大的一个文件。
这段代码跑在node环境中,使用一些node模块。
const fs = require('fs'); //文件系统
const path = require('path');
function findLargest(dir, callback) {
fs.readdir(dir, function (err, files) {
//用fs.readdir读文件夹的内容。传入回调函数
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) => {
//每一个文件都用fs.stat去取状态。取出状态用到回调函数
if (errored) return; // [1]
//发生错误,中断,return掉
if (err) {
errored = true;
return callback(err);
}
stats.push(stat); // [2]
//没有发生错误,把stat文件放入数组
if (--count === 0) { //用count变量来计数,每读完一个文件,都减一,当到0的时候,就认为所有文件都读完了
let largest = stats //然后对数组进行遍历,找出其中
.filter(function (stat) { return stat.isFile(); })
.reduce(function (prev, next) {
if (prev.size > next.size) return prev;
//找出其中最大的文件信息,用下面的callback返回
return next; //记下内容
});
callback(null, files[stats.indexOf(largest)]);
//第一个参数,是否有参数,因为是正常的,所以返回null
}
});
});
});
}
findLargest('./path/to/dir', function (err, filename) {
//使用的时候调用,'传入要查找的目录',传入回调函数
//回调函数接受两个参数。“错误”,“文件名”
if (err) return console.error(err);
//如果有错误,就返回错误信息。
console.log('largest file was:', filename);
//没有错误,返回文件名
});
//在标记的地方有问题,以后补充问题
异步回调有四个问题
- 嵌套层次很深,难以维护
- 无法正常使用return 和 throw
- 无法正常检索堆栈信息
- 多个回调之间难以建立联系
Promise入门
1.Promise简介
new Promise(
/* 执行器 executor */
function (resolve, reject) {
// 一段耗时很长的异步操作
resolve(); // 数据处理完成
reject(); // 数据处理出错
}
)
.then(function A() {
// 成功,下一步
}, function B() {
// 失败,做相应处理
});
2.Promise详解
- Promise是一个代理对象,它和原先要进行的操作并无关系。
- 它通过引入一个回调,避免更多的回调。
3.Promise有三个状态:
- pending [待定] 初始状态
- fulfilled [实现] 操作成功
- rejected [被否决] 操作失败
Promise状态发生改变,就会触发.then()里的响应函数处理后续步骤
Promise状态一经改变,不会再变。
4.范例
- 定时执行
console.log('here we go');
new Promise( resolve => {
setTimeout( () => {
resolve('hello');
}, 2000);
})
.then( value => {
console.log( value + ' world');
});
//输出结果
here we go
hello world //2秒之后输出
5.两步执行范例
- 分两次,顺序依次执行
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');
});
//输出结果
here we go
hello //2秒
world world //2秒
6.已完成的Promise,再执行.then()
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);
//输出结果
start
the promise fulfilled //1秒之后输出
hello, world //再过2秒之后输出
7.在.then()的函数里面不返回新的Promise,会怎么样?
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; //没有等待里面的promise执行
//但是里面的定时器仍在等待执行
//在promise里面如果不直接返回一个promise实例
//就会默认执行下一个环节
//即使返回false也不影响下一组,false作为直接传递到下一组
})
.then( value => {
console.log(value + ' world');
});
//输出结果
here we go //先输出
hello //这三个同时输出
everyone
false worl
Mr.Laurence //最后输出这个
8.then()
- .then()接受两个函数作为参数,分别代表fulfilled和rejected
- .then() 返回一个新的Promise实例,所以它可以链式调用
- 当前面的Promise状态改变时,.then()根据其最终状态,选择特定的状态响应函数执行。
- 状态响应函数可以返回新的promise,或其他值
- 如果返回新的Promise,那么下一级.then()会在新Promise状态改变之后执行。
- 如果返回其他任何值,则会立刻执行下一级.then()
小测试
1. .then()里有.then()的情况
- 因为.then()返回的还是Promise实例
- 会等里面的.then()执行完,在执行外面的。
- 对于我们来说,此时最好将其展开,会更好读。
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');
});
//输出结果
start
Step 1
Step 1-1
Step 1-2
Step 1-3
Step 2
网友评论