所谓"异步" 简单说就是一个任务分红两段, 先执行第一段, 而后转而执行其余任务, 等作好了准备, 再回过头来执行第二段.html
函数既能够做为参数,也能够做为返回值jquery
高阶函数的英文名叫 Higher-Order Function,熟悉 React 的朋友应该知道高阶组件 Higher-Order Component。没错,React 的高阶组件本质上就是高阶函数。编程
那么,什么是高阶函数呢?promise
高阶函数源自于函数式编程,是函数式编程的基本技术。异步
那么,JS做为一门“一切皆为对象”的语言,是如何拥有函数式编程的能力呢?async
是由于在JS中函数是一等公民,即函数能够被赋值给变量,被变量引用,这便使得函数能够做为参数,在其余函数间相互传递:函数式编程
/** * 数值转换 * @param {Number} val 要被处理的数值 * @param {Function} fn 处理输入的val * @return {Number || String} */
const toConvert = function(val, fn) {
return fn(val);
};
const addUnitW = function(val) {
return val + 'W';
};
toConvert(123.1, Math.ceil); // 124
toConvert(123.1, addUnitW); // "123.1W"
复制代码
// 判断一个变量的类型
function isType(type) {
return function (param) {
return Object.prototype.toString.call(param) == `[object ${type}]`
}
}
let isString = isType('String')
let isArray = isType('Array')
console.log(isString('nan')); // true
console.log(isArray({})); // false
复制代码
假设有这样一个需求, 有个函数须要连续调用三次才能够执行, 你能够提早思考一下怎么写异步编程
function eat() {
console.log("吃完了");
}
function after(time, fn) {
let count = 0
return function () {
if(count++ === time) {
fn()
}
}
}
let newEat = after(3, eat)
newEat()
newEat()
newEat()
复制代码
所谓回调函数, 就是把任务的第二段单独写在一个函数里面, 等到从新执行这个任务的时候, 就直接调用这个函数函数
fs.readFile('某个文件', function(err, data) {
if(err) throw err
console.log(data)
})
复制代码
回调函数的问题:布局
// 利用发布订阅的模式, 读取html模板和数据, 当二者都存在时输出
const Event = require('events')
const fs = require('fs')
let eve = new Event()
let html = {}
eve.on('ready', function (key, value) {
html[key] = value
// 当两个结果都有了
if(Object.keys(html).length == 2) {
console.log(html);
}
}) // 注册
fs.readFile('./template.txt','utf8', function (err, template) {
eve.emit('ready', 'data', template)
})
fs.readFile('./data.txt', 'utf8', function (err, data) {
eve.emit('ready', 'data', data)
})
/********************************************/
// 以上这样方式简写的形式能够用一个"哨兵函数来监听"
const fs = require('fs')
/* let html = {} // 哨兵函数 function done (key, value) { html[key] = value if(Object.keys(html).length == 2) { console.log(html); } } **/
// 给哨兵函数封装一下
/**********************************************/
function reader (length, fn) {
let html = {}
return function (key, value) {
html[key] = value
if(Object.keys(html).length == length) {
fn(html)
}
}
}
let done = reader(2, function (html) {
console.log(html)
})
/**********************************************/
fs.readFile('./data.txt','utf8', function (err, template) {
done('template', template)
})
fs.readFile('./demo.txt', 'utf8', function (err, data) {
done('data', data)
})
复制代码
生成器是一个函数, 能够用来生成迭代器
Generator 函数是 ES6 提供的一种异步编程解决方案,整个 Generator 函数就是一个封装的异步任务,或者说是异步任务的容器。异步操做须要暂停的地方,都用 yield 语句注明。
function* gen() {
let a = yield 111;
console.log(a);
let b = yield 222;
console.log(b);
let c = yield 333;
console.log(c);
let d = yield 444;
console.log(d);
}
let t = gen();
//next方法能够带一个参数,该参数就会被看成上一个yield表达式的返回值
t.next(1); //第一次调用next函数时,传递的参数无效
t.next(2); //a输出2;
t.next(3); //b输出3;
t.next(4); //c输出4;
t.next(5); //d输出5;
复制代码
为了让你们更好的理解上面代码是如何执行的,我画了一张图,分别对应每一次的next方法调用:
手写promise
Promise.all的原理
function gen(times, fn) {
return function (i, data) {
if(i+1 == times) {
fn(data)
}
}
}
Promise.all = function (promises) {
return new Promise(function (resolve, reject) {
let done = gen(promises.length, resolve)
for (let i = 0; i < promises.length; i++) {
promises[i].then(function (data) {
done(i, data)
}, reject)
}
})
}
Promise.race的原理:
Promise.race = function (promises) {
return new Promise(function (resolve, reject) {
for (let i = 0; i < promises.length; i++) {
promises[i].then(resolve, reject)
}
})
}
复制代码
q
(早期angular项目中用过) bluebird
这两个库
let fs = require('fs');
let bluebird = require('bluebird'); // bluebird中的一个方法, 可让异步转化为promise
let read = bluebird.promisify(fs.readFile);
read('1.txt','utf-8').then(function(data){
console.log(data);
})
***********实现原理*************
function promisify(fn) {
return function (...args) {
return new Promise(function (resolve, reject) {
fn(...args, function (err, data) {
if (err) reject(err);
resolve(data);
})
})
}
}
复制代码
function *read () {
console.log('开始');
let a = yield readFile('1.txt');
console.log(a)
let b = yield readFile('2.txt');
console.log(=b)
let c = yield readFile('3.txt');
console.log(c)
return c;
}
function co(it){
return new Promise((resolve,reject)=>{
function next(data){
let { value,done } = it.next(data)
if(!done){
value.then((data)=>{
next(data)
},reject)
}else{
resolve(value)
}
}
next()
})
}
复制代码
// 完整代码 也顺便带你们理顺一下
function Promise(executor) {
let self = this;
self.value = undefined; // 成功的值
self.reason = undefined; // 失败的值
self.status = 'pending'; // 目前promise的状态pending
self.onResolvedCallbacks = []; // 可能new Promise的时候会存在异步操做,把成功和失败的回调保存起来
self.onRejectedCallbacks = [];
function resolve(value) { // 把状态更改成成功
if (self.status === 'pending') { // 只有在pending的状态才能转为成功态
self.value = value;
self.status = 'resolved';
self.onResolvedCallbacks.forEach(fn => fn()); // 把new Promise时异步操做,存在的成功回调保存起来
}
}
function reject(reason) { // 把状态更改成失败
if (self.status === 'pending') { // 只有在pending的状态才能转为失败态
self.reason = reason;
self.status = 'rejected';
self.onRejectedCallbacks.forEach(fn => fn()); // 把new Promise时异步操做,存在的失败回调保存起来
}
}
try {
// 在new Promise的时候,当即执行的函数,称为执行器
executor(resolve, reject);
} catch (e) { // 若是执行executor抛出错误,则会走失败reject
reject(e);
}
}
// 这个函数为核心,全部的promise都遵循这个规范
// 主要是处理then中返回的值x和promise2的关系
function resolvePromise(promise2,x,resolve,reject){
// 当promise2和then返回的值x为同一个对象时,变成了本身等本身,会陷入死循环
if(promise2 === x){
return reject(new TypeError('Chaining cycle'));
}
let called;
// x多是一个promise也多是一个普通值
if(x!==null && (typeof x=== 'object' || typeof x === 'function')){
try{
let then = x.then;
if(typeof then === 'function'){
then.call(x,y=>{
if(called) return;
called = true;
resolvePromise(promise2,y,resolve,reject);
},err=>{
if(called) return;
called = true;
reject(err);
});
}else{
resolve(x);
}
}catch(e){
if(called) return;
called = true;
reject(e);
}
}else{
resolve(x);
}
}
// then调用的时候,都是属于异步,是一个微任务
// 微任务会比宏任务先执行
// onFulfilled为成功的回调,onRejected为失败的回调
Promise.prototype.then = function (onFulfilled, onRejected) {
onFulfilled = typeof onFulfilled === 'function'?onFulfilled:val=>val;
onRejected = typeof onRejected === 'function'?onRejected: err=>{throw err}
let self = this;
let promise2;
// 上面讲了,promise和jquery的区别,promise不能单纯返回自身,
// 而是每次都是返回一个新的promise,才能够实现链式调用,
// 由于同一个promise的pending resolve reject只能更改一次
promise2 = new Promise((resolve, reject) => {
if (self.status === 'resolved') {
// 为何要加setTimeout?
// 首先是promiseA+规范要求的
// 其次是你们写的代码,有的是同步,有的是异步
// 因此为了更加统一,就使用为setTimeout变为异步了,保持一致性
setTimeout(()=>{
try { // 上面executor虽然使用try catch捕捉错误
// 可是在异步中,不必定可以捕捉,因此在这里
// 用try catch捕捉
let x = onFulfilled(self.value);
// 在then中,返回值多是一个promise,因此
// 须要resolvePromise对返回值进行判断
resolvePromise(promise2,x,resolve,reject);
} catch (e) {
reject(e);
}
},0)
}
if (self.status === 'rejected') {
setTimeout(()=>{
try {
let x = onRejected(self.reason);
resolvePromise(promise2,x,resolve,reject);
} catch (e) {
reject(e);
}
},0)
}
if (self.status === 'pending') {
self.onResolvedCallbacks.push(() => {
setTimeout(()=>{
try {
let x = onFulfilled(self.value);
resolvePromise(promise2,x,resolve,reject);
} catch (e) {
reject(e);
}
},0)
});
self.onRejectedCallbacks.push(() => {
setTimeout(()=>{
try {
let x = onRejected(self.reason);
resolvePromise(promise2,x,resolve,reject);
} catch (e) {
reject(e);
}
},0)
});
}
});
return promise2
}
Promise.defer = Promise.deferred = function(){
let dfd = {};
dfd.promise = new Promise((resolve,reject)=>{
dfd.resolve = resolve;
dfd.reject = reject;
})
return dfd;
}
module.exports = Promise;
复制代码
async await解决了异步问题:
可让代码像同步
可使用try catch
可使用promise
若是let r1 = await 后面等待的是promise,那么会把promise的结果赋值给前面的r1,若是let r1 = await 后面等待的是普通值,那么就会把这个普通值赋值给前面的r1
设为 Flex 布局之后,子元素的 float 、 clear 和 vertical-align 属性将失效
async函数的实现, 就是将Generator函数和自动执行器,包装到一个函数中
async function read() {
let template = await readFile('./template.txt');
let data = await readFile('data.txt');
return template + '+' + data
}
//等同于
function read () {
return co(function *() {
let template = yield readFile('template.txt');
let data = yield readFile('data.txt');
return template + '+' + data
})
}
复制代码