异步的概念
- Javascript语言的执行环境是"单线程",所谓"单线程",就是指一次只能完成一件任务。如果有多个任务,就必须排队,前面一个任务完成,再执行后面一个任务,以此类推。
- 这种模式的好处是实现起来比较简单;坏处是只要有一个任务耗时很长,后面的任务都必须排队等着,会拖延整个程序的执行。
- 为了解决这个问题,Javascript语言将任务的执行模式分成两种:同步(Synchronous)和异步(Asynchronous)。
- 异步编程的代码不会阻塞后续代码的执行,不是有序执行的,对于操作一些耗时操作执行效率更高
什么时候使用异步
- 事件监听函数的回调
- 定时器函数的回调
- 网络请求
实现异步编程
回调函数
- 有这样一个场景,有两个函数f1,f2,f2的执行需要等待f1的结果,而f1的执行是一个耗时的操作,就可以将f2作为f1的回调函数
function f2(){}
function f1(callback){
setTimeout(()=>{
callback();
})
}
f1(f2);
- 采用这种方式,我们把同步操作变成了异步操作,f1不会堵塞程序运行,相当于先执行程序的主要逻辑,将耗时的操作推迟执行。
- 回调函数的优点是简单、容易理解,缺点是不利于代码的阅读和维护,各个部分之间高度耦合,流程会很混乱,多层的嵌套会造成“回调地狱”。
事件监听
- 采用事件驱动模式。任务的执行不取决于代码的顺序,而取决于某个事件是否发生。
- 下面代码使用jquery语法
f1.on('done', f2);
function f1(){
setTimeout(function () {
f1.trigger('done');
}, 1000);
}
- 这种方法的优点是比较容易理解,可以绑定多个事件,每个事件可以指定多个回调函数,有利于实现模块化
- 缺点是整个程序都要变成事件驱动型,运行流程会变得很不清晰。
发布/订阅
- 我们假定,存在一个"信号中心",某个任务执行完成,就向信号中心"发布"(publish)一个信号,其他任务可以向信号中心"订阅"(subscribe)这个信号,从而知道什么时候自己可以开始执行。这就叫做"发布/订阅模式"(publish-subscribe pattern)。
- 流程应该是先订阅,再发布
jQuery.subscribe("done", f2);
function f1(){
setTimeout(function () {
jQuery.publish("done");
}, 1000);
}
- 这种方法的性质与"事件监听"类似,但是明显优于后者。因为我们可以通过查看"消息中心",了解存在多少信号、每个信号有多少订阅者,从而监控程序的运行。
promise
- 通过new Promise(resolve,reject){}创建一个promise实例,resolve/reject是两个函数,每个promise实例拥有then方法,then方法有两个函数参数,第一个参数是成功状态触发,第二个函数是失败状态触发,当resolve/reject函数执行会触发then函数中的回调函数的执行
使用es5语法实现一个符合promiseA+规范的库
(function () {
var PENDING = "pending",
FULFILLED = "fulfilled",
REJECTED = "rejected";
function Promise(executor) {
if (typeof executor !== "function") throw new TypeError("promise resolve " + executor + " is not a function");
var self = this;
self.PromiseState = PENDING;
self.PromiseResult = undefined;
self.onFulfilledCallbacks = [];
self.onRejectedCallbacks = [];
var run = function (state, result) {
if (self.PromiseState !== PENDING) return;
self.PromiseState = state;
self.PromiseResult = result;
setTimeout(function () {
var callbackChains = state === FULFILLED ? self.onFulfilledCallbacks : self.onRejectedCallbacks;
for (var i = 0; i < callbackChains.length; i++) {
if (typeof callbackChains[i] === "function") {
callbackChains[i](self.PromiseResult);
}
}
});
};
var reject = function reject(reason) {
run(REJECTED, reason);
};
var resolve = function resolve(value) {
if (value instanceof Promise) {
return value.then(resolve, reject);
}
run(FULFILLED, value);
};
try {
executor(resolve, reject);
} catch (error) {
reject(error);
}
}
function resolvePromise(promise2, x, resolve, reject) {
if (x === promise2) {
return reject(new TypeError("Chain cycle deleted for promise #<Promise>"));
}
if ((x !== null && typeof x === "object") || typeof x === "function") {
let called = false;
try {
var then = x.then;
if (typeof then === "function") {
then.call(x, function (y) {
if (called) return;
called = true;
resolvePromise(promise2, y, resolve, reject);
}, function (r) {
if (called) return;
called = true;
reject(r);
});
}
else {
resolve(x);
}
} catch (err) {
if (called) return;
called = true;
reject(err);
}
}
else {
resolve(x);
}
}
Promise.prototype = {
customize: true,
constructor: Promise,
then: function (onFulfilled, onRejected) {
if (typeof onFulfilled !== "function") {
onFulfilled = function (value) {
return value;
};
}
if (typeof onRejected !== "function") {
onRejected = function (reason) {
throw reason;
};
}
var self = this;
var promise = new Promise(function (resolve, reject) {
if (self.PromiseState === FULFILLED) {
setTimeout(function () {
try {
var x = onFulfilled(self.PromiseResult);
resolvePromise(promise, x, resolve, reject);
} catch (error) {
reject(error);
}
});
}
else if (self.PromiseState === REJECTED) {
setTimeout(function () {
try {
var x = onRejected(self.PromiseResult);
resolvePromise(promise, x, resolve, reject);
} catch (error) {
reject(error);
}
});
} else {
self.onFulfilledCallbacks.push(function (PromiseResult) {
setTimeout(function () {
try {
var x = onFulfilled(PromiseResult);
resolvePromise(promise, x, resolve, reject);
} catch (err) {
reject(err);
}
});
});
self.onRejectedCallbacks.push(function (PromiseResult) {
setTimeout(function () {
try {
var x = onRejected(PromiseResult);
resolvePromise(promise, x, resolve, reject);
} catch (err) {
reject(err);
}
});
});
}
});
return promise;
},
catch: function (onRejected) {
return this.then(null, onRejected);
},
finally: function (fn) {
return this.then(data => {
return Promise.resolve(fn()).then(() => data);
}, err => {
return Promise.resolve(fn()).then(() => {
throw err;
});
});
}
};
Promise.resolve = function (value) {
return new Promise(function (resolve) {
resolve(value);
});
};
Promise.reject = function (reason) {
return new Promise(function (_, reject) {
reject(reason);
});
};
Promise.all = function (promiseArr) {
return new Promise(function (resolve, reject) {
var index = 0,
ret = [];
for (var i = 0; i < promiseArr.length; i++) {
(function (i) {
var proItem = promiseArr[i];
if (!(proItem instanceof Promise)) {
index++;
ret[i] = proItem;
if (index === promiseArr.length) {
resolve(ret);
}
return;
}
proItem.then(function (result) {
index++;
ret[i] = result;
if (index === promiseArr.length) {
resolve(ret);
}
}).catch(function (err) {
reject(err);
});
}(i));
}
});
};
Promise.race = function (promiseArr) {
return new Promise((resolve, reject) => {
for (var i = 0; i < promiseArr.length; i++) {
(function (i) {
var proItem = promiseArr[i];
if (!(proItem instanceof Promise)) {
resolve(proItem);
} else {
proItem.then(function (result) {
resolve(result);
}, function (err) {
reject(err);
});
}
}(i));
}
});
};
if (typeof globalThis === "object") globalThis.Promise = Promise;
else if (typeof window === "object" && window.window === window) window.Promise = Promise;
else if (typeof global === "object") global.Promise = Promise;
}());
promise的并发操作 createRequestPool函数实现
const delay = function (interval) {
return new Promise(function (resolve, reject) {
if (interval === 1000) {
reject(new Error(interval));
}
setTimeout(function () {
resolve(interval);
}, interval);
});
};
const tasks = [
() => delay(1002),
() => delay(1004),
() => delay(100),
() => delay(1302),
() => delay(1092),
() => delay(1024)
];
function createRequestPool(tasks, pool = 5) {
let togetherPool = new Array(pool).fill(0);
let index = 0;
let result = [];
togetherPool = togetherPool.map(() => {
return new Promise(function (resolve, reject) {
const run = function () {
if (index >= tasks.length) {
resolve();
return;
}
let old_index = index;
const task = tasks[index++];
task().then(function (res) {
result[old_index] = res;
run();
}).catch(function (err) {
reject(err);
});
};
run();
});
});
return Promise.all(togetherPool).then(() => result);
}
let res = createRequestPool(tasks, 2).then(res => {
console.log(res);
}).catch(err => {
console.log(err);
});
面试题技巧
- promise在面试过程中也是常考的
- 首先要弄清
事件循环、宏任务、微任务的概念
- 遇到
setTimeout,等待时间到了就会将回调放入宏任务队列中
- 遇到
promise,promise的回调函数execute会同步执行,then的回调函数会放到微任务队列中
- 遇到async await,
await下面的代码相当于promise实例中then的回调函数,放到微任务中
- 执行流程
- 首先执行同步任务
- 同步任务执行过程中会将异步任务放到
宏队列/微队列中
- 同步任务完成后,首先按照入队顺序执行微任务队列的任务,如果过程中产生了新的异步任务,则将新的异步任务加入对应的任务队列
- 同步任务和微任务队列清空后,首先按照入队顺序执行宏任务队列的任务,如果这个过程中产生了新的微任务,会将新的微任务放到微队列中,执行完同步代码后,要先去
清空微队列,当没有同步任务和微任务后,才能继续进行清空宏任务队列操作
- 注意每次执行一个宏任务前,都要
保证微任务队列是空的,有微任务就先执行微任务
做做题吧
promise面试题连接,点击跳转~